package ar.net.Blisk.XXActivityStarter; //========================================================================= //package com.google.appinventor.components.runtime; import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; import android.text.TextUtils; import android.util.Log; import com.google.appinventor.components.annotations.*; import com.google.appinventor.components.runtime.*; import com.google.appinventor.components.common.*; import com.google.appinventor.components.annotations.DesignerComponent; import com.google.appinventor.components.annotations.DesignerProperty; import com.google.appinventor.components.annotations.IsColor; import com.google.appinventor.components.annotations.PropertyCategory; import com.google.appinventor.components.annotations.SimpleObject; import com.google.appinventor.components.annotations.SimpleProperty; import com.google.appinventor.components.common.ComponentCategory; import com.google.appinventor.components.common.PropertyTypeConstants; import com.google.appinventor.components.common.YaVersion; import com.google.appinventor.components.runtime.errors.YailRuntimeError; import com.google.appinventor.components.runtime.util.AnimationUtil; import com.google.appinventor.components.runtime.util.ErrorMessages; import com.google.appinventor.components.runtime.util.NougatUtil; import com.google.appinventor.components.runtime.util.YailList; import com.google.appinventor.components.runtime.util.TextViewUtil; import com.google.appinventor.components.annotations.SimpleEvent; import com.google.appinventor.components.annotations.SimpleFunction; import com.google.appinventor.components.annotations.SimpleProperty; import android.util.Log; import android.widget.LinearLayout; import android.widget.TextView; import java.util.ArrayList; import com.google.appinventor.components.annotations.SimpleProperty; import java.util.List; import java.io.File; import android.view.View; import android.content.Intent; import android.content.Context; //========================================================================= import com.google.appinventor.components.runtime.AndroidNonvisibleComponent; import com.google.appinventor.components.runtime.ComponentContainer; import com.google.appinventor.components.runtime.errors.YailRuntimeError; import com.google.appinventor.components.runtime.util.YailList; @DesignerComponent(version = 1, category = ComponentCategory.EXTENSION, description = "", nonVisible = true, iconName = "https://blisk.net.ar/apps/images/XXActivityStarter.png" ) @SimpleObject(external = true) public class XXActivityStarter extends AndroidNonvisibleComponent implements ActivityResultListener, Component, Deleteable{ private String action; private String dataUri; private String dataType; private String activityPackage; private String activityClass; private String extraKey; private String extraValue; private String resultName; private Intent resultIntent; private String result; private int requestCode; private YailList extras; private final ComponentContainer container; private static final String LOG_TAG = "XXActivityStarter"; //private ComponentContainer container; private Context context; public XXActivityStarter(ComponentContainer container) { super(container.$form()); this.container = container; result = ""; Action(Intent.ACTION_MAIN); ActivityPackage(""); ActivityClass(""); DataUri(""); DataType(""); ExtraKey(""); ExtraValue(""); Extras(new YailList()); ResultName(""); } /** * Returns the action that will be used to start the activity. */ @SimpleProperty(category = PropertyCategory.BEHAVIOR) public String Action() { return action; } /** * Specifies the action that will be used to start the activity. */ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_STRING, defaultValue = "") @SimpleProperty public void Action(String action) { this.action = action.trim(); } /** * Returns the extra key that will be passed to the activity. * Obsolete. Should use Extras instead */ @SimpleProperty( description = "Returns the extra key that will be passed to the activity.\n" + "DEPRECATED: New code should use Extras property instead.", category = PropertyCategory.BEHAVIOR) public String ExtraKey() { return extraKey; } /** * Specifies the extra key that will be passed to the activity. * Obsolete. Should use Extras instead */ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_STRING, defaultValue = "") @SimpleProperty public void ExtraKey(String extraKey) { this.extraKey = extraKey.trim(); } /** * Returns the extra value that will be passed to the activity. * Obsolete. Should use Extras instead */ @SimpleProperty( description = "Returns the extra value that will be passed to the activity.\n" + "DEPRECATED: New code should use Extras property instead.", category = PropertyCategory.BEHAVIOR) public String ExtraValue() { return extraValue; } /** * Specifies the extra value that will be passed to the activity. * Obsolete. Should use Extras instead */ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_STRING, defaultValue = "") @SimpleProperty public void ExtraValue(String extraValue) { this.extraValue = extraValue.trim(); } /** * Returns the name that will be used to retrieve a result from the activity. */ @SimpleProperty(category = PropertyCategory.BEHAVIOR) public String ResultName() { return resultName; } /** * Specifies the name that will be used to retrieve a result from the * activity. */ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_STRING, defaultValue = "") @SimpleProperty public void ResultName(String resultName) { this.resultName = resultName.trim(); } /** * Returns the result from the activity. */ @SimpleProperty(category = PropertyCategory.BEHAVIOR) public String Result() { return result; } /** * Returns the data URI that will be used to start the activity. */ @SimpleProperty(category = PropertyCategory.BEHAVIOR) public String DataUri() { return dataUri; } /** * Specifies the data URI that will be used to start the activity. */ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_STRING, defaultValue = "") @SimpleProperty public void DataUri(String dataUri) { this.dataUri = dataUri.trim(); } /** * Returns the MIME type to pass to the activity. */ @SimpleProperty(category = PropertyCategory.BEHAVIOR) public String DataType() { return dataType; } /** * Specifies the MIME type to pass to the activity. */ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_STRING, defaultValue = "") @SimpleProperty public void DataType(String dataType) { this.dataType = dataType.trim(); } /** * Returns the package part of the specific component that will be started. */ @SimpleProperty(category = PropertyCategory.BEHAVIOR) public String ActivityPackage() { return activityPackage; } /** * Specifies the package part of the specific component that will be started. */ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_STRING, defaultValue = "") @SimpleProperty public void ActivityPackage(String activityPackage) { this.activityPackage = activityPackage.trim(); } /** * Returns the class part of the specific component that will be started. */ @SimpleProperty( category = PropertyCategory.BEHAVIOR) public String ActivityClass() { return activityClass; } /** * Specifies the class part of the specific component that will be started. */ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_STRING, defaultValue = "") @SimpleProperty public void ActivityClass(String activityClass) { this.activityClass = activityClass.trim(); } /** * Event raised after this `ActivityStarter` returns. * * @param result The result returned by the activity */ @SimpleEvent(description = "Event raised after this ActivityStarter returns.") public void AfterActivity(String result) { EventDispatcher.dispatchEvent(this, "AfterActivity", result); } /** * Event raised if this `ActivityStarter returns because the activity was canceled. */ @SimpleEvent(description = "Event raised if this ActivityStarter returns because the activity was canceled.") public void ActivityCanceled() { EventDispatcher.dispatchEvent(this, "ActivityCanceled"); } /** * Returns the MIME type from the activity. */ @SimpleProperty(category = PropertyCategory.BEHAVIOR) public String ResultType() { if (resultIntent != null) { String resultType = resultIntent.getType(); if (resultType != null) { return resultType; } } return ""; } /** * Returns the URI from the activity. */ @SimpleProperty(category = PropertyCategory.BEHAVIOR) public String ResultUri() { if (resultIntent != null) { String resultUri = resultIntent.getDataString(); if (resultUri != null) { return resultUri; } } return ""; } /** * Specifies the list of key-value pairs that will be passed as extra data to the activity. */ @SimpleProperty public void Extras(YailList pairs) { for (Object pair : pairs.toArray()) { boolean isYailList = pair instanceof YailList; boolean isPair = isYailList ? ((YailList) pair).size() == 2 : false; if (!isYailList || !isPair) { throw new YailRuntimeError("Argument to Extras should be a list of pairs", "ActivityStarter Error"); } } extras = pairs; } /** * Returns the list of key-value pairs that will be passed as extra data to the activity. */ @SimpleProperty public YailList Extras() { return extras; } /** * Returns the name of the activity that corresponds to this `ActivityStarter`, * or an empty string if no corresponding activity can be found. */ @SimpleFunction(description = "Returns the name of the activity that corresponds to this " + "ActivityStarter, or an empty string if no corresponding activity can be found.") public String ResolveActivity() { Intent intent = buildActivityIntent(); PackageManager pm = container.$context().getPackageManager(); ResolveInfo resolveInfo = pm.resolveActivity(intent, 0); if (resolveInfo != null && resolveInfo.activityInfo != null) { return resolveInfo.activityInfo.name; } return ""; } /** * Start the activity corresponding to this `ActivityStarter`. */ @SimpleFunction(description = "Start the activity corresponding to this ActivityStarter.") public void StartActivity() { resultIntent = null; result = ""; Intent intent = buildActivityIntent(); if (requestCode == 0) { // First time, we need to register this as an ActivityResultListener with the Form. // The Form's onActivityResult method will be called when the activity returns. If we // register with the Form and then use the requestCode when we start an activity, the Form // will call our resultReturned method. requestCode = form.registerForActivityResult(this); } if (intent == null) { form.dispatchErrorOccurredEvent(this, "StartActivity", ErrorMessages.ERROR_ACTIVITY_STARTER_NO_ACTION_INFO); } else { try { container.$context().startActivityForResult(intent, requestCode); String openAnim = container.$form().OpenScreenAnimation(); AnimationUtil.ApplyOpenScreenAnimation(container.$context(), openAnim); } catch (ActivityNotFoundException e) { form.dispatchErrorOccurredEvent(this, "StartActivity", ErrorMessages.ERROR_ACTIVITY_STARTER_NO_CORRESPONDING_ACTIVITY); } } } private Intent buildActivityIntent() { Uri uri = (dataUri.length() != 0) ? Uri.parse(dataUri) : null; Intent intent = new Intent(action); if (uri != null && dataUri.toLowerCase().startsWith("file://")) { Log.d(LOG_TAG, "Using file://"); File file = new File(uri.getPath()); if (file.isFile()) { Log.d(LOG_TAG, "It's a file"); uri = NougatUtil.getPackageUri(form, file); intent = new Intent(action); intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); Log.d(LOG_TAG, "added permissions"); // adb log shows this gets printed } } if (TextUtils.isEmpty(Action())) { return null; } if (dataType.length() != 0) { if (uri != null) { intent.setDataAndType(uri, dataType); } else { intent.setType(dataType); } } else { intent.setData(uri); } if (activityPackage.length() != 0 || activityClass.length() != 0) { ComponentName component = new ComponentName(activityPackage, activityClass); intent.setComponent(component); } else if (Action().equals("android.intent.action.MAIN")) { return null; } if (extraKey.length() != 0 && extraValue.length() != 0) { Log.i(LOG_TAG, "Adding extra, key = " + extraKey + " value = " + extraValue); //========================================================================== // The following code was added to the original ActivityStarter code // It was created by user "pepemont" and the idea is that any key starting // with "B." (an upper case B followed by a dot) or "l." (a lower case L // followed by a dot) will be passed as boolean or long respectively. //========================================================================== if (extraKey.startsWith("B.")) { extraKey = extraKey.substring(2); intent.putExtra(extraKey, Byte.parseByte(extraValue)); } else if (extraKey.startsWith("l.")) { extraKey = extraKey.substring(2); intent.putExtra(extraKey, Long.parseLong(extraValue)); } else intent.putExtra(extraKey, extraValue); //========================================================================== // End of addition. The original code just had the following line instead of // the conditional statemets above: // // intent.putExtra(extraKey, extraValue); //========================================================================== } // If the extra value is a string, put it to the intent. If the extra value is a list // of strings, convert it to a java list and put that to the intent. for (Object extra : extras.toArray()) { YailList castExtra = (YailList) extra; String key = castExtra.getString(0); Object value = castExtra.getObject(1); Log.i(LOG_TAG, "Adding extra, key = " + key + " value = " + value); if ((key.length() != 0)) { if (value instanceof YailList) { Log.i(LOG_TAG, "Adding extra list, key = " + key + " value = " + value); intent.putExtra(key, ((YailList) value).toStringArray()); } else { String stringValue = castExtra.getString(1); Log.i(LOG_TAG, "Adding extra string, key = " + key + " value = " + stringValue); //========================================================================== // The following code was added to the original ActivityStarter code // It was created by user "pepemont", and the idea is that any key starting // with "B." (an upper case B followed by a dot) or "l." (a lower case L // followed by a dot) will be passed as boolean or long respectively. //========================================================================== if (key.startsWith("B.")) { key = key.substring(2); intent.putExtra(key, Byte.parseByte(stringValue)); } else if (key.startsWith("l.")) { key = key.substring(2); intent.putExtra(key, Long.parseLong(stringValue)); } else intent.putExtra(key, stringValue); //========================================================================== // End of addition. The original code just had the following line instead of // the conditional statemets above: // // intent.putExtra(key, stringValue); //========================================================================== } } ; } ; return intent; } @Override public void resultReturned(int requestCode, int resultCode, Intent data) { if (requestCode == this.requestCode) { Log.i(LOG_TAG, "resultReturned - resultCode = " + resultCode); if (resultCode == Activity.RESULT_OK) { resultIntent = data; if (resultName.length() != 0 && resultIntent != null && resultIntent.hasExtra(resultName)) { result = resultIntent.getStringExtra(resultName); } else { result = ""; } // call user's AfterActivity event handler AfterActivity(result); } else if (resultCode == Activity.RESULT_CANCELED) { ActivityCanceled(); } } } @SimpleEvent(description = "The ActivityError event is no longer used. " + "Please use the Screen.ErrorOccurred event instead.", userVisible = false) public void ActivityError(String message) { } // Deleteable implementation @Override public void onDelete() { form.unregisterForActivityResult(this); } }