Skip to content

Add fully-automatic mode to the Upload Widget #113

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 13, 2020
Merged
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
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package cloudinary.android.sample;

import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import androidx.test.platform.app.InstrumentationRegistry;

import androidx.test.espresso.intent.rule.IntentsTestRule;
import androidx.test.platform.app.InstrumentationRegistry;

import com.cloudinary.android.sample.R;
import com.cloudinary.android.sample.app.MainActivity;
import com.cloudinary.android.uploadwidget.UploadWidget;

import org.junit.BeforeClass;
import org.junit.Ignore;
Expand All @@ -20,11 +19,10 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.intent.Intents.intending;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent;
import static androidx.test.espresso.matcher.ViewMatchers.withId;

public class UploadWidgetTest {
Expand All @@ -44,13 +42,10 @@ public static void setup() throws IOException {
@Ignore
@Test
public void testUploadWidget() {
Intent intent = new Intent();
intent.setData(Uri.fromFile(assetFile));
Instrumentation.ActivityResult result = new Instrumentation.ActivityResult(Activity.RESULT_OK, intent);

intending(hasComponent(MockNativePickerActivity.class.getName())).respondWith(result);
intentsTestRule.getActivity().startActivityForResult(new Intent(intentsTestRule.getActivity(), MockNativePickerActivity.class), MainActivity.CHOOSE_IMAGE_REQUEST_CODE);

UploadWidget.startActivity(intentsTestRule.getActivity(),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we test the other options?

MainActivity.UPLOAD_WIDGET_REQUEST_CODE,
new UploadWidget.Options(UploadWidget.Action.START_NOW,
Collections.singletonList(Uri.fromFile(assetFile))));
onView(withId(R.id.crop_action)).perform(click());
onView(withId(R.id.doneButton)).perform(click());
onView(withId(R.id.uploadFab)).perform(click());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,32 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.provider.DocumentsContract;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.tabs.TabLayout;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;

import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.util.Pair;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.core.util.Pair;
import androidx.viewpager.widget.ViewPager;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.RecyclerView;
import androidx.appcompat.widget.Toolbar;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import androidx.viewpager.widget.ViewPager;

import com.cloudinary.android.MediaManager;
import com.cloudinary.android.UploadRequest;
import com.cloudinary.android.sample.R;
import com.cloudinary.android.sample.core.CloudinaryHelper;
import com.cloudinary.android.sample.model.Resource;
import com.cloudinary.android.sample.persist.ResourceRepo;
import com.cloudinary.android.uploadwidget.UploadWidget;
import com.cloudinary.utils.StringUtils;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.tabs.TabLayout;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.PicassoTools;

Expand All @@ -50,8 +49,7 @@ public class MainActivity extends AppCompatActivity implements ResourcesAdapter.
public static final int IN_PROGRESS_PAGE_POSITION = 2;
public static final int FAILED_PAGE_POSITION = 3;

public static final int CHOOSE_IMAGE_REQUEST_CODE = 1000;
private static final int UPLOAD_WIDGET_REQUEST_CODE = 1002;
public static final int UPLOAD_WIDGET_REQUEST_CODE = 1002;
private FloatingActionButton fab;
private BroadcastReceiver receiver;
private ViewPager pager;
Expand All @@ -72,7 +70,7 @@ protected void onCreate(Bundle savedInstanceState) {
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
UploadWidget.openMediaChooser(MainActivity.this, CHOOSE_IMAGE_REQUEST_CODE);
UploadWidget.startActivity(MainActivity.this, UPLOAD_WIDGET_REQUEST_CODE);
}
});

Expand Down Expand Up @@ -223,8 +221,6 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
upload((Resource) data.getSerializableExtra(ImageActivity.RESOURCE_INTENT_EXTRA));
} else if (requestCode == UPLOAD_WIDGET_REQUEST_CODE) {
handleUploadWidgetResult(data);
} else if (requestCode == CHOOSE_IMAGE_REQUEST_CODE && data != null) {
UploadWidget.startActivity(this, UPLOAD_WIDGET_REQUEST_CODE, extractImageUris(data));
}
}
}
Expand All @@ -250,11 +246,9 @@ private void handleUploadWidgetResult(final Intent data) {
public void run() {
ArrayList<UploadWidget.Result> results = data.getParcelableArrayListExtra(UploadWidget.RESULT_EXTRA);
for (UploadWidget.Result result : results) {
UploadRequest uploadRequest = UploadWidget.preprocessResult(MainActivity.this, result);
String requestId = uploadRequest.dispatch(MainApplication.get());

Resource resource = createResourceFromUri(result.uri, data.getFlags());
resource.setRequestId(requestId);
resource.setRequestId(result.requestId);
ResourceRepo.getInstance().resourceQueued(resource);
}
}
Expand Down Expand Up @@ -324,14 +318,7 @@ public void run() {
}

private Resource createResourceFromUri(final Uri uri, final int flags) {
final int takeFlags = flags & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

if (DocumentsContract.isDocumentUri(MainActivity.this, uri)) {
getContentResolver().takePersistableUriPermission(uri, takeFlags);
}

Pair<String, String> pair = Utils.getResourceNameAndType(MainActivity.this, uri);

return new Resource(uri.toString(), pair.first, pair.second);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.cloudinary.android.UploadRequest;
import com.cloudinary.android.uploadwidget.model.CropPoints;
import com.cloudinary.android.uploadwidget.ui.UploadWidgetActivity;

import java.util.ArrayList;
import java.util.Collection;

/**
* Helper class to start the UploadWidget and preprocess its results.
Expand All @@ -30,16 +33,47 @@ public class UploadWidget {
*/
public static final String URIS_EXTRA = "uris_extra";

public static final String ACTION_EXTRA = "required_action_extra";

/**
* Start the {@link UploadWidgetActivity}. Please make sure that you have declared it your manifest.
* Start the {@link UploadWidgetActivity} with a pre-populated list of files to upload, and return
* a list of upload request to dispatch. This is equivalent to RequiredAction.NONE.
* Deprecated - please use {@link #startActivity(Activity, int, Options)} directly.
*
* @param activity The activity which requested the upload widget.
* @param requestCode A request code to start the upload widget with.
* @param uris Uris of the selected media files.
* @param uris Uris of the selected media files.
*/
@Deprecated
public static void startActivity(@NonNull Activity activity, int requestCode, @NonNull ArrayList<Uri> uris) {
Intent intent = new Intent(activity, UploadWidgetActivity.class);
intent.putParcelableArrayListExtra(URIS_EXTRA, uris);
startActivity(activity, requestCode, new Options(Action.NONE, uris));
}

/**
* Start the {@link UploadWidgetActivity} configured for full process - Launch file selection UI
* as well as dispatching the created upload request automatically.
*
* @param activity The activity which requested the upload widget.
* @param requestCode A request code to start the upload widget with.
*/
public static void startActivity(@NonNull Activity activity, int requestCode) {
startActivity(activity, requestCode, new Options(Action.DISPATCH, null));
}

/**
* Start the {@link UploadWidgetActivity} configured according to the supplied launch options.
*
* @param activity The activity which requested the upload widget.
* @param requestCode A request code to start the upload widget with.
* @param options The launch option to define the required upload widget behaviour
*/
public static void startActivity(@NonNull Activity activity, int requestCode, Options options) {
Intent intent = new Intent(activity, UploadWidgetActivity.class).putExtra(ACTION_EXTRA, options.action);

if (options.uris != null && !options.uris.isEmpty()) {
intent.putParcelableArrayListExtra(URIS_EXTRA, new ArrayList<Parcelable>(options.uris));
}

activity.startActivityForResult(intent, requestCode);
}

Expand Down Expand Up @@ -76,9 +110,9 @@ public static UploadRequest preprocessResult(Context context, Result result) {
* Preprocess the {@code uploadRequest}'s with the upload widget results.
*
* @param uploadRequest Already constructed upload request.
* @param result Result data from the upload widget.
* @param result Result data from the upload widget.
* @return Preprocessed {@link UploadRequest}
* @throws IllegalStateException if {@code uploadRequest} was already dispatched.
* @throws IllegalStateException if {@code uploadRequest} was already dispatched.
*/
public static UploadRequest preprocessResult(Context context, @NonNull UploadRequest uploadRequest, Result result) {
return UploadWidgetResultProcessor.process(context, uploadRequest, result);
Expand All @@ -93,7 +127,9 @@ public static UploadRequest preprocessResult(Context context, @NonNull UploadReq
public static void openMediaChooser(Activity activity, int requestCode) {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[]{"image/jpeg", "image/jpg", "image/png", "video/*"});
Expand Down Expand Up @@ -133,6 +169,13 @@ public static final class Result implements Parcelable {
*/
public int rotationAngle;

/**
* The request id, in case the full flow was requested and an upload request was already
* started or dispatched.
*/
public String requestId;


public Result(Uri uri) {
this.uri = uri;
}
Expand All @@ -142,6 +185,7 @@ public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(uri, flags);
dest.writeParcelable(cropPoints, flags);
dest.writeInt(rotationAngle);
dest.writeString(requestId);
}

public static final Creator<Result> CREATOR = new Creator<Result>() {
Expand All @@ -160,6 +204,7 @@ protected Result(Parcel in) {
uri = in.readParcelable(Uri.class.getClassLoader());
cropPoints = in.readParcelable(CropPoints.class.getClassLoader());
rotationAngle = in.readInt();
requestId = in.readString();
}

@Override
Expand All @@ -168,4 +213,47 @@ public int describeContents() {
}
}

/**
* This class is used to define the required launch behaviour of the upload widget.
*/
public static class Options {
final Action action;
final Collection<Uri> uris;

/**
* Construct a new instance to use when launching the upload widget activity.
*
* @param action Indicates the widget how to handle the selected files. This also
* affects the result received later in onActivityResult. When the action
* used is DISPATCH or START_NOW the widget returns a list of request IDs.
* When the action is NONE, the widget returns results that needs to be
* processed into UploadRequest, allowing customization before dispatching/starting.
* @param uris A list of Uris of files to display and upload.
*/
public Options(@NonNull Action action, @Nullable Collection<Uri> uris) {
this.action = action;
this.uris = uris;
}
}

/**
* Define how the upload widget handles the selected files to upload
*/
public enum Action {
/**
* Dispatch the selected files within the upload widget, and return request IDs.
*/
DISPATCH,

/**
* Immediately start the selected files within the upload widget, and return request IDs.
*/
START_NOW,

/**
* Create the request data and preprocess configuration without starting any request.
*/
NONE,
}

}
Loading