How to wire up an Share intent in MAUI for Android? - maui

I'm trying to work out how to wire up a Share intent in Android using MAUI.
Am I running into preview bugs, or am I not doing it correctly?
I'm running MAUI Preview 10, with Visual Studio 17.1.0 Preview 1.0.
Version 1
I've added the share intent to the AndroidManifest.xml file:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
<application android:allowBackup="true" android:icon="#mipmap/appicon" android:roundIcon="#mipmap/appicon_round" android:supportsRtl="true">
<activity android:name="SomeLongName.MainActivity" >
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
</application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>
And then by adding a Name property to the MainActivity class:
[Activity(Name = "SomeLongName.MainActivity", Theme = "#style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize)]
public class MainActivity : MauiAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
if (Intent.Type == "text/plain")
{
}
}
}
When I debug the application in MAUI, the app builds correctly and the Output says that the app has deployed. However, in Visual Studio, the debugger fails to run, and there is no evidence of the app on the emulator.
Version 2
In version 2, I've tried to create a separate activity for the Share intent:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
<application android:allowBackup="true" android:icon="#mipmap/appicon" android:roundIcon="#mipmap/appicon_round" android:supportsRtl="true">
<activity android:name="SomeLongName.ShareActivity" >
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
</application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>
[Activity(Name = "SomeLongName.ShareActivity")]
public class ShareActivity : MauiAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
if (Intent.Type == "text/plain" && Intent.Action == "android.intent.action.SEND")
{
//handleSendUrl();
}
base.OnCreate(savedInstanceState);
}
I can now successfully debug the application, but when I try to share to the application, I get an exception on the base.OnCreate call:
Java.Lang.IllegalStateException: 'The specified child already has a parent. You must call removeView() on the child's parent first.'

Seems the issue centres around themes. The solution is in the original question, apply Version 2. With some minor updates. In the MainApplication class add an attribute defining the theme (same as in MainActivity class but could be different).
[Application(Theme = "#style/Maui.SplashTheme")]
public class MainApplication : MauiApplication
{
public MainApplication(IntPtr handle, JniHandleOwnership ownership)
: base(handle, ownership)
{
}
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}
This both works without error and enables debugging.
Without the MainApplication theme setting, when sharing to the android app (incoming), an error thrown:
Java.Lang.IllegalArgumentException: 'The style on this component requires your app theme to be Theme.MaterialComponents (or a descendant).'
In my secondary activity, the Activity attribute sets the Theme, but is ignored and error still shown, hence the Application level needed. Not sure why.
[Activity(Theme = "#android:style/Theme.MaterialComponents.Light", Name = "com.somename.receiveurlactivity", MainLauncher = false, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize)]
public class ReceiveUrlActivity : MauiAppCompatActivity
{
//.... etc

Related

AndroidAlarmManagerPlus not starting automatically after reboot

I've set permissions in the AndroidManifest.xml file
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
I've also added the following lines in the AndroidManifest.xml as specified in the docs
<service
android:name="dev.fluttercommunity.plus.androidalarmmanager.AlarmService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="false"/>
<receiver
android:name="dev.fluttercommunity.plus.androidalarmmanager.AlarmBroadcastReceiver"
android:exported="false"/>
<receiver
android:name="dev.fluttercommunity.plus.androidalarmmanager.RebootBroadcastReceiver"
android:enabled="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"></action>
</intent-filter>
</receiver>
In my main.dart file, I've the following lines of codes...
import 'package:android_alarm_manager_plus/android_alarm_manager_plus.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await AndroidAlarmManager.initialize();
runApp(MyApp());
await AndroidAlarmManager.periodic(Duration(minutes: 1), 0, syncData);
}
void syncData() async {
print("synching data");
String? uploadedUrl = await S3.uploadToS3();
print(uploadedUrl);
}
What should I add for this to work automatically on reboot? TIA :)
From Android 8.0 You can't use most of the implicit broadcast ,
So your application service will not start on
ACTION_LOCKED_BOOT_COMPLETED or ACTION_BOOT_COMPLETED
for more details
you can use WorkManager instead to sync data to server
here is a good example

URL Bar is not Hiding

We are developing PWA application to submit it to Play Store. We used TWA, followed all concepts but somehow we cant manage to hide the URL BAR.
Digital asset links are configured and tested. We have connected website with TWA, we have successfully tested mapping editor, we have added intent filter, association with assetlinks.json was successfully tested
Android Manifest
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="eu.clubforceone.mobile">
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/appName"
android:supportsRtl="true"
android:theme="#style/Theme.LauncherActivity">
<meta-data
android:name="asset_statements"
android:resource="#string/assetStatements" />
<activity
android:name="android.support.customtabs.trusted.LauncherActivity"
android:label="#string/appName">
<tools:validation testUrl="https://login.clubforce.eu" />
<meta-data
android:name="android.support.customtabs.trusted.DEFAULT_URL"
android:value="#string/launchUrl" />
<meta-data
android:name="android.support.customtabs.trusted.STATUS_BAR_COLOR"
android:resource="#color/colorPrimary" />
<meta-data
android:name="android.support.customtabs.trusted.SPLASH_IMAGE_DRAWABLE"
android:resource="#drawable/splash" />
<meta-data
android:name="android.support.customtabs.trusted.SPLASH_SCREEN_BACKGROUND_COLOR"
android:resource="#color/whiteColor" />
<meta-data
android:name="android.support.customtabs.trusted.SPLASH_SCREEN_FADE_OUT_DURATION"
android:value="300" />
<meta-data
android:name="android.support.customtabs.trusted.FILE_PROVIDER_AUTHORITY"
android:value="#string/providerAuthority" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="login.clubforce.eu" />
</intent-filter>
</activity>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="#string/providerAuthority"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/filepaths" />
</provider>
<service
android:name="android.support.customtabs.trusted.TrustedWebActivityService"
android:enabled="#bool/enableNotification"
android:exported="#bool/enableNotification">
<intent-filter>
<action android:name="android.support.customtabs.trusted.TRUSTED_WEB_ACTIVITY_SERVICE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
</application>
</manifest>
build.gradle module app
apply plugin: 'com.android.application'
def twaManifest = [
applicationId: 'eu.clubforceone.mobile',
hostName: 'login.clubforce.eu', // The domain being opened in the TWA.
launchUrl: '/', // The start path for the TWA. Must be relative to the domain.
name: 'Clubforce', // The name shown on the Android Launcher.
themeColor: '#283795', // The color used for the status bar.
backgroundColor: '#F6F6F6', // The color used for the splash screen background.
whiteColor: '#FFFFFF', // The color used for the splash screen background.
enableNotifications: false // Set to true to enable notification delegation
]
android {
compileSdkVersion 28
defaultConfig {
applicationId "eu.clubforceone.mobile"
minSdkVersion 16
targetSdkVersion 28
versionCode 20003
versionName '2.2'
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
// The name for the application on the Android Launcher
resValue "string", "appName", twaManifest.name
// The URL that will be used when launching the TWA from the Android Launcher
resValue "string", "launchUrl", "https://" + twaManifest.hostName + twaManifest.launchUrl
// The hostname is used when building the intent-filter, so the TWA is able to
// handle Intents to open https://svgomg.firebaseapp.com.
resValue "string", "hostName", twaManifest.hostName
// This variable below expresses the relationship between the app and the site,
// as documented in the TWA documentation at
// https://developers.google.com/web/updates/2017/10/using-twa#set_up_digital_asset_links_in_an_android_app
// and is injected into the AndroidManifest.xml
resValue "string", "assetStatements",
'[{ \\"relation\\": [\\"delegate_permission/common.handle_all_urls\\"],' +
'\\"target\\": {\\"namespace\\": \\"web\\", \\"site\\": \\"https://' +
twaManifest.hostName + '\\"}}]'
// This attribute sets the status bar color for the TWA. It can be either set here or in
// `res/values/colors.xml`. Setting in both places is an error and the app will not
// compile. If not set, the status bar color defaults to #FFFFFF - white.
resValue "color", "colorPrimary", twaManifest.themeColor
// Sets the color for the background used for the splash screen when launching the
// Trusted Web Activity.
resValue "color", "backgroundColor", twaManifest.backgroundColor
// used only for specific situation where white color is needed
resValue "color", "whiteColor", twaManifest.whiteColor
// Defines a provider authority fot the Splash Screen
resValue "string", "providerAuthority", twaManifest.applicationId + '.fileprovider'
// The enableNotification resource is used to enable or disable the
// TrustedWebActivityService, by changing the android:enabled and android:exported
// attributes
resValue "bool", "enableNotification", twaManifest.enableNotifications.toString()
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
productFlavors {
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.github.GoogleChrome.custom-tabs-client:customtabs:91b4a1270b511ce70245d3440e6267762c5f1c6b'
}
assetLinks.json
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "eu.clubforceone.mobile",
"sha256_cert_fingerprints":
["CD:D5:D1:54:AC:8F:17:0B:8A:E6:5D:0F:C2:07:1D:FC:D7:2C:16:EE:66:A4:D9:F6:DA:1C:57:12:1B:57:A5:F9"]
}
}]
I just have no idea what to do...
This is my result from running "keytool -list -printcert -jarfile app-release.apk"
Signer #1:
Signature:
Owner: deleted for privacy reason
Issuer: deleted for privacy reason
Serial number: 6beb859e
Valid from: Sun Apr 01 14:33:45 CEST 2018 until: Thu Aug 17 14:33:45 CEST 2045
Certificate fingerprints:
MD5: 29:FC:96:6C:FC:D0:E4:69:BC:BC:B1:95:01:DA:6D:2D
SHA1: 00:7E:76:27:F9:5E:51:83:6A:77:70:57:90:A1:B7:56:66:66:A3:99
SHA256: CD:D5:D1:54:AC:8F:17:0B:8A:E6:5D:0F:C2:07:1D:FC:D7:2C:16:EE:66:A4:D9:F6:DA:1C:57:12:1B:57:A5:F9
Signature algorithm name: SHA256withRSA
Version: 3
Extensions:
#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 06 A2 63 AE E8 BD 9A 56 00 66 4E 8B 9C 09 EA 13 ..c....V.fN.....
0010: 20 C8 E5 4E ..N
]
]
You could use PWA2APK to simplify the procedure:
PWA2APK
The 'package_name' field in the 'assetlinks.json' file needs to point to the application package-name (which is the same as the applicationId).
Here's what the assetlinks.json file should look like:
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "eu.clubforceone.mobile",
"sha256_cert_fingerprints":
["CD:D5:D1:54:AC:8F:17:0B:8A:E6:5D:0F:C2:07:1D:FC:D7:2C:16:EE:66:A4:D9:F6:DA:1C:57:12:1B:57:A5:F9"]
}
}]
If you're letting the Play store manage your signatures, check that your assetlinks.json contains the SHA215 from your Play store settings rather than the one generated by keytool. As described in this other answer.
That said, I'm in the same situation as you. I also can't remove the URL bar from my TWA app.

UWP and FullTrustProcessLauncher missing in namaspeace

I am trying to launch powershell (some lightweight commands that all user can do) command from UWP application using FullTrustProcessLauncher.
Problem is I am getting that this name does not exists in current context.
My manifest is largly default so it looks like :
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
IgnorableNamespaces="uap mp">
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate"/>
</Resources>
<Applications>
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="PrototypeProject.App">
<Extensions>
<desktop:Extension Category="windows.fullTrustProcess" Executable="powershell.exe">
<desktop:FullTrustProcess>
<desktop:ParameterGroup GroupId="TestGroup" Parameters="ls"/>
</desktop:FullTrustProcess>
</desktop:Extension>
</Extensions>
</Application>
</Applications>
<Capabilities>
<Capability Name="internetClient" />
<rescap:Capability Name="runFullTrust"/>
</Capabilities>
</Package>
And class I am trying to call it from looks like:
using Windows.ApplicationModel;
using Windows.Foundation;
namespace FullTrustProcess
{
public class Ftp
{
public const string FtpParamGroupId = "TestGroup";
public void RunFtp()
{
IAsyncAction operation = FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync(FtpParamGroupId);
}
}
}
Now am I missing something??
It looks like I was missing - Windows Desktop Extensions for the UWP, in extensions reference

#Before Suite and #BeforeTest methods are not called if groups are executed in TestNG

Below is my XML file and Demo Class. Precondition() method will run before demo1() method and postCondition() method will run after demo1() method. Same process is for demo2(). But when i run the code, BeforeSuite and BeforeTest Methods are not Called. Why.? How to call them ?
Output :
Before Method is executing
DEMO -1
After Method is executing
Before Method is executing
DEMO 2
After Method is executing
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
<test name="Test">
<groups>
<run>
<include name = "Hey"></include>
</run>
</groups>
<classes>
<class name="practicepackage.Demo"/>
</classes>
</test> <!-- Test -->
</suite> <!-- Suite -->
package practicepackage;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
public class Demo {
#BeforeSuite
public void beforeSuite(){
System.out.println("Before Suite method is being launched");
}
#BeforeTest
public void beforeTest(){
System.out.println("Before Test Method is being luanched");
}
#BeforeMethod(groups = {"Hey"})
public void PreCondition(){
System.out.println("Before Method is executing");
}
#Test (groups = {"Hey"})
public void demo1(){
System.out.println("DEMO -1 ");
}
#Test(groups = {"Hey"})
public void demo2(){
System.out.println("DEMO 2");
}
#AfterMethod(groups = {"Hey"})
public void postCondition(){
System.out.println("After Method is executing");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
<test name="Test">
<groups>
<run>
<include name = "Hey"></include>
</run>
</groups>
<classes>
<class name="practicepackage.Demo"/>
</classes>
</test> <!-- Test -->
</suite> <!-- Suite -->
In order to ensure that #BeforeSuite and #BeforeTest are executed all the time, please enable the attribute alwaysRun=true for these annotations.
This is required because when you run through groups, these configuration methods wont be selected by TestNG until and unless they are part of the group which you selected.
Group selection in TestNG can be visualised as a sort of filtering mechanism in TestNG that lets you tell TestNG the criteria for filtering, when it decides which tests to run.

Scringo Login status changes not received on Android

I'm trying to implement Scringo Login status change on Android. But my Broadcast receiver is never called.
I've followed the instructions described on http://www.scringo.com/docs/android-guides/popular/handling-login-status-changes/
So I registered my broadcast receiver:
<receiver android:name="com.jino.footster.MyReceiver">
<intent-filter>
<action android:name="com.scringo.LoginBroadcast" />
</intent-filter>
</receiver>
And I then defined my Broacast receiver:
package com.jino.footster;
import com.scringo.utils.ScringoLogger;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("com.scringo.LoginBroadcast")) {
boolean isLogin = intent.getExtras().getBoolean("isLogin");
String accountId = intent.getExtras().getString("accountId");
ScringoLogger.e("Got Login receiver: " + isLogin + ", " + accountId);
}
}
}
When I start the application the login seems to be successful: I see the below message in logcat:
04-24 01:12:35.000: I/Scringo(4717): Your Scringo user token is: a03fgalc5E
However, the onReceive method of my broadcast receiver is never called.
Would someone be able to help?
thank you
You forgot the category:
<receiver android:name="com.jino.footster.MyReceiver">
<intent-filter>
<action android:name="com.scringo.LoginBroadcast" />
<category android:name="com.jino.footster"/>
</intent-filter>
</receiver>