Please help me about the ActivityResultListener

Hello !

I want to start an Intent (in sync, UI Thread) in my extension. But I can't understand what to extend where to extend what to @Override, etc.

Pls someone give me an example of using Activity.

A small Draft ->

public class ClassName extends AndroidNonvisibleComponent implements ActivityResultListener {
    
    public ClassName(ComponentContainer c){
        super(c.$form);
    }
    
    public void methodRunIntent(){
        Intent i = new Intent(/*some properties*/);
        // What to do next ? registerForActivity or startActivityForResult ?
    }
    
    @Override
    // Which method do I override ? resultReturned or onActivityResult or something else ?
}

you may check the source code of ImagePicker

1 Like

I had checked that and I had become confused after checking that and I'm trying to do something quite toooooo similar. But ImagePicker has so many superclasses, causing me to get confused.

ImagePicker extends Picker extends ButtonBase extends <i forgot it>

Then check Camera component

1 Like

Please give me an example these are very confusing. :disappointed_relieved:

Or atleast complete my code by telling what methods to use instead of those two single-lime comments.

You can extend at most one class but can implement numerous classes by overriding the required methods.
You should check ai2 sources before doing something complicated. You'll always find your answers there.

1 Like

I know but I want to say that,

ImagePicker extends Picker
Picker extends ButtonBase
ButtonBase extends

I said it in shortcut.

That completely changed the meaning of your statement.

Even here, one class is extending only one class.
You are allowed to extend a subclass.

Do you want any result to be returned? Or want to just start an intent?

If your intent wants to start an activity
form.startActivity(#intent);
1 Like

I want a result to be returned. (I want to pick an image.)

I don't know why the code doesn't work and I'll send the snippet in a while.

Here it is :


    public void pick(){
        Intent pick = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.INTERNAL_CONTENT_URI);
        pick.setType("image/*");
        BeforePicking();
        container.$context().startActivityForResult(pick, 1);
    }


    @Override
    public void resultReturned(int requestCode, int resultCode, Intent data){
        if (requestCode == 1){
            if (resultCode == form.RESULT_OK){
                selectionUri = data.getData();
                if (selectionUri == null){
                    selectedImage = "";
                    NullUriException();
                }
                else {
                    selectedImage = selectionUri.toString();
                    AfterPicking(selectedImage);
                }
            }
            else if (resultCode == form.RESULT_CANCELED){
                ImagePickingCancelled();
            }
        }
    }

The Activity is started, I pick the image, but no event is raised. Strange. :thinking:

maybe add this lines in pick():

if (requestCode == 0) {
      requestCode = form.registerForActivityResult(this);
    }

from Camera.java

The answer is here!

import android.content.Context;
import android.util.Log;
import android.app.Activity;
import android.widget.Toast;
import android.content.Intent;
import android.net.Uri;
import android.provider.MediaStore;
import android.os.Bundle;
import android.os.Handler;
import com.google.appinventor.components.annotations.*;
import com.google.appinventor.components.runtime.*;
import com.google.appinventor.components.runtime.Form;
import com.google.appinventor.components.runtime.ActivityResultListener;
import com.google.appinventor.components.annotations.DesignerComponent;
import com.google.appinventor.components.annotations.DesignerProperty;
import com.google.appinventor.components.annotations.PropertyCategory;
import com.google.appinventor.components.annotations.SimpleEvent;
import com.google.appinventor.components.annotations.SimpleFunction;
import com.google.appinventor.components.annotations.SimpleObject;
import com.google.appinventor.components.annotations.SimpleProperty;
import com.google.appinventor.components.common.ComponentCategory;

@DesignerComponent(version = 1,  description = "",
        category = ComponentCategory.EXTENSION,
        nonVisible = true,   iconName = "http://appyBuilder.com/extensions/icons/extension.png")

@SimpleObject(external = true)
public class PickTools extends AndroidNonvisibleComponent implements ActivityResultListener {
    private ComponentContainer container;
    private Context context; 
    private Form form;
    private Activity activity;
    private int requestCode = 0;
    
    public PickTools(ComponentContainer container) {
        super(container.$form());
        this.container = container;
        context = container.$context();
        form = container.$form();
        activity = (Activity) container.$context();
    }
  
  
    @SimpleFunction(description = "Stores a value in TinyDB")
    public void OpenGallery() {
      
      Intent gallery = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.INTERNAL_CONTENT_URI);
      if(requestCode == 0) {
        requestCode = form.registerForActivityResult(this);
      }
      activity.startActivityForResult(gallery, requestCode);  
    }
  
   @Override
   public void resultReturned(int requestCode, int resultCode, Intent data){
      if (resultCode == Activity.RESULT_OK && requestCode == this.requestCode){
         Uri imageUri = data.getData();
         AfterPicking(imageUri.toString());
      }
   }
  
    @SimpleEvent(description = "")
    public void AfterPicking(String selection) {
      EventDispatcher.dispatchEvent(this, "AfterPicking", selection);
    }
}
1 Like

Thanks. It works.

But I have a problem. The selectedImage is a content:// path. But I want a normal file path.

When I use toString(), it returns a content:// path, and when I use getPath() it returns the same with the initial content:// removed. Please help me with this.

Ok now try to add this code, and see how it works

File file = new File(uri.getPath());//create path from uri
final String[] split = file.getPath().split(":");//split the path.
filePath = split[1];//assign it to a string(your choice).
2 Likes

Didn't work.

Used the short-form of the code.

selectedImage = new File(selectionUri.getPath()).getPath();

Didn't split at the end btw, as it wont be possible. Cuz the result upto this is doesn't contain a :. (As per test in Companion.)

1 Like

Why do you need the actual path, to access files you would need to use the uri.

1 Like

Ya but still, using actual file path has many advantages. Not listing them here though.

I remember @vknow360 having a method in one of his extensions to convert contentUri to filepath. Can you help please ?

You can:

  1. copy file from selected uri to asd temporarily with Content Resolver and return path from ASD ( see SAF source)
  2. let MediaUtil do it for you (see ImagePicker source)
    MediaUtil.copyMediaToTempFile(container.$form(), selectionURI);
  3. ask user to use FileTools or TaifunFile extension
2 Likes