Unable to get my Xamarin.Android app to fire Toast after boot. I checked many accepted solutions but none seem to resolve my problem. I've also tried various "working" examples but haven't had any luck so clearly I'm missing something.
Device: Samsung Galaxy S3
API: 19
Android Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.novak" android:versionCode="1" android:versionName="1.0" android:installLocation="auto">
<uses-sdk android:minSdkVersion="16" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application android:label="ReceiverApp">
<receiver android:enabled="true"
android:exported="true"
android-permission="android.permission.RECEIVE_BOOT_COMPLETED"
android:name="com.novak.BootReceiver" >
<intent-filter >
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
</application>
</manifest>
BootReceiver.cs
using Android.App;
using Android.Widget;
using Android.Content;
namespace ReceiverApp
{
[BroadcastReceiver]
[IntentFilter(new[] { Intent.ActionBootCompleted })]
public class BootReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
Toast.MakeText(context, "Receiver", ToastLength.Long).Show();
}
}
}
It would appear that what I want to do is not possible. Once my app is installed the app sits in a stopped state and must be executed manually the first time before it can receive broadcasts
[How to start a Service when .apk is Installed for the first time
P.S: On real device it takes a while to fire the event, ie: 2 minutes after unlock the screen on my Samsung. On emulator it takes very short.
I wrote the code below to notice when it will fire:
Autorun.cs - Just add this file into your project, that's it, it will start after the boot. No manifest modification needed.
using Android;
using Android.App;
using Android.Content;
// we need to ask permission to be notified of these events
[assembly: UsesPermission (Manifest.Permission.ReceiveBootCompleted)]
namespace XamarinCookbook
{
// we want this to fire when the device boots
[BroadcastReceiver]
[IntentFilter (new []{ Intent.ActionBootCompleted })]
public class ServiceStarter : BroadcastReceiver
{
public override void OnReceive (Context context, Intent intent)
{
#region Start chrome
var mesaj = "Autorun started";
Android.Widget.Toast.MakeText(Android.App.Application.Context, mesaj, Android.Widget.ToastLength.Long).Show();
var uri = Android.Net.Uri.Parse("https://500px.com");
var intent1 = new Intent(Intent.ActionView, uri);
intent1.AddFlags(ActivityFlags.NewTask);
intent1.SetPackage("com.android.chrome");
try
{
context.StartActivity(intent1);
}
catch (ActivityNotFoundException ex)
{
//Chrome browser not installed
intent.SetPackage(null);
context.StartActivity(intent1);
}
#endregion
/*
#region Real code
// just start the service
var myIntent = new Intent (context, typeof(XamarinService));
context.StartService (myIntent);
#endregion
*/
}
}
}
VS Solution:
https://drive.google.com/open?id=1iYZQ2YCvBkyym9-2FvU7KXoBKUWsMdlT
Related
Preconditions:
Appium 1.21.0
TestNG 7.4.0
Im trying to run a test class in 2 simulator. But when executing the allSuites.xml it runs the 2 test class in the same device (iPhone 4 Plus). It completely ignores the first device (iPhone 14 - although the class I want to run in this device is run in the second device).
I've been trying every solution on web but there's not so much to follow, can anybody help me please?
Here's the allSuitesParallel.xml:
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="AllSuitesParallel" verbose="3" parallel="tests" thread-count="2">
<suite-files suite="1">
<parameter name="wda" value="8100"/>
<parameter name="deviceName" value="iPhone 14"/>
<parameter name="port" value="5555"/>
<suite-file path="suitesFolder/onboardingAndDiscoverSuites/discoverMeatSuite.xml"/>
</suite-files>
<suite-files suite="2">
<parameter name="wda" value="8101"/>
<parameter name="deviceName" value="iPhone 14 Plus"/>
<parameter name="port" value="4726"/>
<suite-file path="suitesFolder/onboardingAndDiscoverSuites/discoverMeatSuite.xml"/>
</suite-files>
</suite>
This is the base
public static AppiumDriverLocalService service;
public static IOSDriver<MobileElement> driver;
private static final String APP_LOCATION = [app Location];
#Parameters({"wda","deviceName","port"})
#BeforeSuite
public static void setUp(String wda, String deviceName, String port){
try {
service = new AppiumServiceBuilder().usingPort(Integer.valueOf(port)).build();
service.start();
DesiredCapabilities caps = new DesiredCapabilities();
//LOCAL
caps.setCapability(MobileCapabilityType.PLATFORM_VERSION, "16.0");
caps.setCapability(MobileCapabilityType.PLATFORM_NAME, "IOS");
caps.setCapability(MobileCapabilityType.DEVICE_NAME, deviceName);
caps.setCapability(IOSMobileCapabilityType.WDA_LOCAL_PORT, wda);
caps.setCapability("automationName", "XCUITest");
caps.setCapability("maxTypingFrequency", "7");
//--Experiments
//caps.setCapability("fullReset", false);
//caps.setCapability("noReset", true);
//caps.setCapability("appium:shouldTerminateApp", true);
//--App
caps.setCapability(MobileCapabilityType.APP, APP_LOCATION);
//--Driver
URL url = new URL("http://127.0.0.1:4723/wd/hub");
driver = new IOSDriver<MobileElement>(url, caps);
} catch (Exception exp) {
System.out.println("Cause is : " + exp.getCause());
System.out.println("Message is : " + exp.getMessage());
exp.printStackTrace();
}
Also, I enable Appium by adding 'appium' in the terminal from the IDE Intellij:
User iOSTasty % appium
[Appium] Welcome to Appium v1.22.3
[Appium] Appium REST http interface listener started on 0.0.0.0:4723
I followed this guy: https://www.youtube.com/watch?v=SfPcxjFERKU&t=308s&ab_channel=TechTockTech
and his xml is not working for me
I'm currently using firebase messaging cloud to push notification for my app. I'm trying to make a custom notification sound for the push notification. I believe that it can be done by putting "sound: blabla.mp3" inside the payload, but how do i define the sound inside dart page?
Use flutter_local_notifications package
AndroidNotificationDetails androidNotificationsDetails = AndroidNotificationDetails(
'your other channel id',
'your other channel name',
'your other channel description',
importance: Importance.Max,
priority: Priority.Max,
enableLights: true,
playSound: true,
sound: RawResourceAndroidNotificationSound('notification'),
);
Note: you should add notification.mp3 file in android/app/src/main/res/raw/notification.mp3 and don't forget to specify playSound:
playSound: true,
this worked for me in the foreground / background and when the app is closed.
It can be done flutter_local_notifications plugin.
First, you should define channels for both Android and iOS:
const androidPlatformChannel = AndroidNotificationDetails(
'ANDROID_CHANNEL_ID',
'Name',
'Description',
color: Color.fromARGB(255, 0, 0, 0),
importance: Importance.max,
sound: RawResourceAndroidNotificationSound('notification_sound'),
playSound: true,
priority: Priority.high,
showWhen: false,
);
const iOSPlatformChannel = IOSNotificationDetails(
sound: 'notification_sound.aiff',
presentAlert: true,
presentBadge: true,
presentSound: true,
);
const platformChannel = NotificationDetails(
android: androidPlatformChannel,
iOS: iOSPlatformChannel,
);
Then show a notification:
await flutterLocalNotificationsPlugin.show(
id,
title,
body,
platformChannel,
payload: notification.payload,
);
IMPORTANT! notification_sound.aiff file should be copied with XCode
you can do this simply by calling the sound and playing it in the firebase configure method.
widget._firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
print('on message $message');
AudioCache player = new AudioCache();
const alarmAudioPath = "sounds/notification.mp3";
player.play(alarmAudioPath);
},
onResume: (Map<String, dynamic> message) async {
print('on resume $message');
},
onLaunch: (Map<String, dynamic> message) async {
print('on launch $message');
},
);
this wouldnt be effective as the file wouldnt be played if the app is in the background
To create a custom notification sound for both Android you will need to predefine a Notification Channel for your app. I used Flutter Local Notifications plugin to do this. In there, you can specify the parameters for your Android Notification Channel such as
var androidPlatformChannelSpecifics = AndroidNotificationDetails(
'your other channel id',
'your other channel name',
'your other channel description',
sound: RawResourceAndroidNotificationSound('slow_spring_board');
Note: slow_spring_board.mp3 is stored in the android\app\src\main\res\raw section of your app. Do not include the file extension when calling RawResourceAndroidNotificationSound()
There are other steps required to get it setup for iOS devices (mp3 are not supported). Once this is all done, you can begin working on your cloud messaging portion, in there you'll have to reference the notification channel id, and the sound file. I recommend including the audio file extension in that code.
You must create notification channel first with following code
Application.kt
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.graphics.Color;
import android.os.Build;
import io.flutter.app.FlutterApplication
import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugins.GeneratedPluginRegistrant
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService
import io.flutter.plugins.pathprovider.PathProviderPlugin;
import io.flutter.plugin.common.MethodChannel
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
import android.net.Uri;
import android.media.AudioAttributes;
import android.content.ContentResolver;
class Application : FlutterApplication(), PluginRegistrantCallback {
override fun onCreate() {
super.onCreate()
this.createChannel()
FlutterFirebaseMessagingService.setPluginRegistrant(this)
}
override fun registerWith(registry: PluginRegistry?) {
// createChannel();
FirebaseCloudMessagingPluginRegistrant.registerWith(registry);
}
private fun createChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Create the NotificationChannel
val name: String = getString(R.string.default_notification_channel_id)
val channel = NotificationChannel(name, "default", NotificationManager.IMPORTANCE_HIGH)
val soundUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://"+ getApplicationContext().getPackageName() + "/raw/sample");
val att = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.build();
channel.setSound(soundUri, att)
val notificationManager: NotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
/*
val id = mapData["id"]
val name = mapData["name"]
val descriptionText = mapData["description"]
val sound = "sample"
val importance = NotificationManager.IMPORTANCE_HIGH
val mChannel = NotificationChannel(id, name, importance)
mChannel.description = descriptionText
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(mChannel)
completed = true
*/
}
}
}
And android manifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.foodlz.orders">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.VIBRATE" />
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<application
android:name=".Application"
android:label="test App"
android:icon="#mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="#style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- This keeps the window background of the activity showing
until Flutter renders its first frame. It can be removed if
there is no splash screen (such as the default splash screen
defined in #style/LaunchTheme). -->
<meta-data
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
android:value="true" />
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="FLUTTER_NOTIFICATION_CLICK" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="#string/default_notification_channel_id"/>
<meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="#drawable/ic_appstore" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="#color/colorPrimary" />
</application>
</manifest>
finally you must put your notification sound.wav file in res/raw
that's it
The Consent SDK allows to show a consent form which, however, it is currently in English only (version 1.0.3 of the SDK). SDK page says:
To update consent text of the Google-rendered consent form, modify the consentform.html file included in the Consent SDK as required.
However, consentform.html is provided as an asset and I don't see a way to localize it, especially using gradle. What is the best way to handle localization in this case? And why this was not done in the first place? Europe is not just English.
Since Google's EU Consent dialog is not localizable, I created my own consent dialog which you can translate as usual with strings.xml. It's loosely based on what Google did. This is to be used without mediation:
You are free to use my code, however consult your legal advisor, if the text is appropriate for you. I cannot provide legal advice on the consent text that is appropriate for you.
Add to your gradle file:
implementation 'com.google.android.ads.consent:consent-library:1.0.3'
Add member variables:
public boolean mShowNonPersonalizedAdRequests = false;
private AlertDialog mEuDialog;
In onCreate() call checkConsentStatus():
#Override
protected void onCreate(Bundle savedInstanceState) {
// ...
checkConsentStatus();
// ...
}
Add checkConsentStatus() method which uses Google's Consent SDK:
// https://developers.google.com/admob/android/eu-consent
private void checkConsentStatus(){
ConsentInformation consentInformation = ConsentInformation.getInstance(this);
ConsentInformation.getInstance(this).addTestDevice("YOUR-DEVICE-ID"); // enter your device id, if you need it for testing
String[] publisherIds = {"pub-YOUR-ADMOB-PUB-ID"}; // enter your admob pub-id
consentInformation.requestConsentInfoUpdate(publisherIds, new ConsentInfoUpdateListener() {
#Override
public void onConsentInfoUpdated(ConsentStatus consentStatus) {
log("User's consent status successfully updated: " +consentStatus);
if (ConsentInformation.getInstance(MainActivity.this).isRequestLocationInEeaOrUnknown()){
log("User is from EU");
/////////////////////////////
// TESTING - reset the choice
//ConsentInformation.getInstance(MainActivity.this).setConsentStatus(ConsentStatus.UNKNOWN);
/////////////////////////////
// If the returned ConsentStatus is UNKNOWN, collect user's consent.
if (consentStatus == ConsentStatus.UNKNOWN) {
showMyConsentDialog(false);
}
// If the returned ConsentStatus is PERSONALIZED or NON_PERSONALIZED
// the user has already provided consent. Forward consent to the Google Mobile Ads SDK.
else if (consentStatus == ConsentStatus.NON_PERSONALIZED) {
mShowNonPersonalizedAdRequests = true;
// The default behavior of the Google Mobile Ads SDK is to serve personalized ads.
// If a user has consented to receive only non-personalized ads, you can configure
// an AdRequest object with the following code to specify that only non-personalized
// ads should be returned.
}
} else {
log("User is NOT from EU");
// we don't have to do anything
}
}
#Override
public void onFailedToUpdateConsentInfo(String errorDescription) {
log("User's consent status failed to update: " +errorDescription);
}
});
}
Add showMyConsentDialog() method:
public void showMyConsentDialog(boolean showCancel) {
AlertDialog.Builder alertDialog = new AlertDialog.Builder(MainActivity.this, R.style.MyAlertDialogStyle);
LayoutInflater inflater = getLayoutInflater();
View eu_consent_dialog = inflater.inflate(R.layout.eu_consent, null);
alertDialog.setView(eu_consent_dialog)
.setCancelable(false);
if (showCancel) alertDialog.setPositiveButton(R.string.dialog_close, null);
mEuDialog = alertDialog.create();
mEuDialog.show();
Button btn_eu_consent_yes = eu_consent_dialog.findViewById(R.id.btn_eu_consent_yes);
Button btn_eu_consent_no = eu_consent_dialog.findViewById(R.id.btn_eu_consent_no);
Button btn_eu_consent_remove_ads = eu_consent_dialog.findViewById(R.id.btn_eu_consent_remove_ads);
btn_eu_consent_yes.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mEuDialog.cancel();
toast(getString(R.string.thank_you), MainActivity.this);
ConsentInformation.getInstance(MainActivity.this).setConsentStatus(ConsentStatus.PERSONALIZED);
mShowNonPersonalizedAdRequests = false;
}
});
btn_eu_consent_no.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mEuDialog.cancel();
toast(getString(R.string.thank_you), MainActivity.this);
ConsentInformation.getInstance(MainActivity.this).setConsentStatus(ConsentStatus.NON_PERSONALIZED);
mShowNonPersonalizedAdRequests = true;
}
});
btn_eu_consent_remove_ads.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mEuDialog.cancel();
IAP_buyAdsFree(); // YOUR REMOVE ADS METHOD
}
});
TextView tv_eu_learn_more = eu_consent_dialog.findViewById(R.id.tv_eu_learn_more);
tv_eu_learn_more.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
euMoreInfoDialog();
}
});
}
This is the consent layout, save to eu_consent.xml:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<LinearLayout
android:id="#+id/ll_eu_consent"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="#dimen/activity_horizontal_margin"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/eu_consent_text"
android:textSize="14sp"
android:paddingBottom="6dp"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/eu_consent_question"
android:textSize="14sp"
android:paddingBottom="6dp"
android:textStyle="bold"
/>
<Button
android:id="#+id/btn_eu_consent_yes"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/eu_consent_yes"
android:textSize="13sp"
/>
<Button
android:id="#+id/btn_eu_consent_no"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/eu_consent_no"
android:textSize="13sp"
android:layout_marginTop="6dp"
android:layout_marginBottom="6dp"
/>
<Button
android:id="#+id/btn_eu_consent_remove_ads"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/action_remove_ads"
android:textSize="13sp"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/eu_consent_change_setting"
android:textSize="14sp"
android:paddingTop="6dp"
android:paddingBottom="6dp"
/>
<TextView
android:id="#+id/tv_eu_learn_more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/learn_more"
android:textSize="14sp"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:textColor="#color/blue"
style="#style/SelectableItem"
/>
</LinearLayout>
</ScrollView>
Add euMoreInfoDialog():
private void euMoreInfoDialog(){
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this, R.style.MyAlertDialogStyle);
ScrollView sv = new ScrollView(this);
LinearLayout ll = new LinearLayout(this);
ll.setOrientation(LinearLayout.VERTICAL);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
params.setMargins(40, 20, 40, 20);
TextView tv_my_privacy_policy = new TextView(this);
String link = ""+getResources().getString(R.string.app_name)+"";
tv_my_privacy_policy.setText(Html.fromHtml(link));
tv_my_privacy_policy.setMovementMethod(LinkMovementMethod.getInstance());
ll.addView(tv_my_privacy_policy, params);
TextView tv_google_partners = new TextView(this);
tv_google_partners.setText(R.string.google_partners);
tv_google_partners.setPadding(40,40,40,20);
ll.addView(tv_google_partners);
List<AdProvider> adProviders = ConsentInformation.getInstance(this).getAdProviders();
for (AdProvider adProvider : adProviders) {
//log("adProvider: " +adProvider.getName()+ " " +adProvider.getPrivacyPolicyUrlString());
link = ""+adProvider.getName()+"";
TextView tv_adprovider = new TextView(this);
tv_adprovider.setText(Html.fromHtml(link));
tv_adprovider.setMovementMethod(LinkMovementMethod.getInstance());
ll.addView(tv_adprovider, params);
}
sv.addView(ll);
builder.setTitle(R.string.privacy_policy)
.setView(sv)
.setPositiveButton(R.string.dialog_close, null);
final AlertDialog createDialog = builder.create();
createDialog.show();
}
In your AdMob web interface select the ad technology providers you wish to use. I suggest you don't select more than 20 (or so), because I assume the euMoreInfoDialog() will become very slow if you select too many providers.
Add to onDestroy() to prevent errors on screen rotate:
#Override
public void onDestroy(){
// ...
if (mEuDialog != null && mEuDialog.isShowing()) mEuDialog.cancel();
// ...
super.onDestroy();
}
When you make an ad request, check the value of mShowNonPersonalizedAdRequests and add "npa" to the request if necessary:
Bundle extras = new Bundle();
if (mShowNonPersonalizedAdRequests)
extras.putString("npa", "1");
AdRequest adRequest = new AdRequest.Builder()
.addTestDevice(AdRequest.DEVICE_ID_EMULATOR)
.addTestDevice("YOUR-DEVICE-ID-GOES-HERE") // insert your device id
.addNetworkExtrasBundle(AdMobAdapter.class, extras)
.build();
And lastly, add strings for all your languages to strings.xml:
<!-- EU GDPR Consent texts -->
<string name="eu_consent_text">Dear user!\n\nWe use Google Admob to show ads. Ads support our work, and enable further development of this app. In line with the new European Data Protection Regulation (GDPR), we need your consent to serve ads tailored for you.</string>
<string name="eu_consent_question">Can your data be used to show ads tailored for you?</string>
<string name="learn_more">Learn how your data is used</string>
<string name="google_partners">Google and its partners:</string>
<string name="eu_consent_yes">Yes, continue to show relevant ads</string>
<string name="eu_consent_no">No, show ads that are irrelevant</string>
<string name="eu_consent_change_setting">You can change this setting anytime in the \"About\" window.</string>
<string name="thank_you">Thank you!</string>
That's it!
(Note: log() and toast() are my methods, replace them with your own. PRIVACY_URL is your String url to your privacy policy.)
In Android Studio select the Project files view, then go to External Libraries> then look for consent library, then click by right mouse button on classes.dex and choose Show in Explorer. Next go to upper folder and there search for assets folder and consetform.html, there are two folders for that library - maybe, for debug and release build? But I just found out it works.
edit: now it not works, due to android studio update, this solution works: https://stackoverflow.com/a/51310779/1555754
I took the ConsentFormClasses.jar and created an own MyConsentFormClasses.jar. In this jar-File I took the class 'ConsentForm.class' and added a new method load(String locale).
Difference to the original method load()is the call of the webiew.
this.webView.loadUrl("file:///android_asset/consentform.html")
changed to
this.webView.loadUrl("file:///android_asset/consentform_"+locale+".html")
The string variable locale I defined as string resource.
In the Assests folder I put the following files e.g consentform_en.html, consentform_fr.html, consentform_pl.html.
Building the new ConsentForm.Class und create MyConsentFormClasses.jar, copied the jar in the libsfolder of my project and configured AndroidStudio dependencies...
The disadvantage to have to make changes in the source code is bearable for me. I hope that Google will provide a proper solution here soon
GGK
I'm new to android, so this might be prety basic.
I'm trying to write data into a file, yet I can not find the file in the device.
I'm trying to get the file in the following directory:
"Computer\Nexus 5\Internal storage\Android\data\application name"
I have tried different method but none of them has worked:
FileOutputStream stream;
try {
stream = openFileOutput(filename,Context.MODE_APPEND | Context.MODE_WORLD_READABLE);
stream.write(string.getBytes());
stream.close();
}catch(IOException e){
}}
and
String path = Environment.getExternalStorageDirectory().getAbsolutePath()+"/Android/data/" + packageName + "/files/";
try {
boolean exists = (new File(path )).exists();
if (!exists) {
new File(path ).mkdirs();
}
// Open output stream
FileOutputStream fOut = new FileOutputStream(path + filename,true);
// write integers as separated ascii's
fOut.write((Integer.valueOf(content).toString() + " ").getBytes());
// Close output stream
fOut.flush();
fOut.close();
} catch (IOException e) {
e.printStackTrace();
}
I have added the permission in the manifest :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.proLeague"
android:versionCode="1"
android:versionName="1.0" >
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE"/>
<group gid="sdcard_rw" />
<group gid="media_rw" />
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="21" />
<application
android:allowBackup="true"
android:debuggable= "true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
I thought maybe the file is logged properly but I need to pull it out from the ADB, is that possible?
I had manage to solve my issues on my own :
Evantually the code was fine, the problem was that on my nexus 5 phone I need to reboot the phone before I search for new files in the flash storage.
I saw in some blogs it is pretty common.
I am learning Auto-update feature of AIR application. I created simple application which is not working. code is below:
AutoUpdate.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationComplete="checkForUpdate()" >
<fx:Script><![CDATA[
import air.update.ApplicationUpdaterUI;
import air.update.events.UpdateEvent;
private var appUpdater:ApplicationUpdaterUI=new ApplicationUpdaterUI();
private function checkForUpdate():void
{
setApplicationVersion();
appUpdater.delay = 1;
appUpdater.isDownloadProgressVisible = true;
appUpdater.isDownloadUpdateVisible = true
appUpdater.isInstallUpdateVisible = true;
appUpdater.isFileUpdateVisible = true;
appUpdater.updateURL = "http://localhost:8081/DynamicWeb/release/update.xml";
appUpdater.isCheckForUpdateVisible = false;
appUpdater.addEventListener(UpdateEvent.INITIALIZED, onUpdate);
appUpdater.addEventListener(ErrorEvent.ERROR, onError);
appUpdater.initialize();
}
}
// Find the current version for our Label below
private function setApplicationVersion():void
{
var appXML:XML = NativeApplication.nativeApplication.applicationDescriptor;
var ns:Namespace = appXML.namespace();
lbl.text = "Current version is " + appXML.ns::version;
//Common.helpDetails = appXML.ns::versionLabel;
}
private function onError(event:ErrorEvent):void
{
//Alert.show(event.toString());
}
private function onUpdate(event:UpdateEvent):void
{
appUpdater.checkNow(); // Go check for an update now }
}
]]></fx:Script>
<s:HGroup x="89" y="124" width="413" height="34">
<s:Label id="lbl" />
</s:HGroup>
</s:WindowedApplication>
*Initially in AutoUpdate-app.xml and update.xml file version tag is set to 1.0.0
* server side update.xml file is :
<?xml version="1.0" encoding="UTF-8"?>
<update xmlns="http://ns.adobe.com/air/framework/update/description/1.0">
<version>1.0.0</version>
<url>http://localhost:8081/DynamicWeb/release/Update_air.air</url>
<description><![CDATA[
Typically, this is used to summarize what's new in the release
]]></description>
</update>
Now I have exported application and installed it. After that i changed version on both file AutoUpdate-app.mxml and update.xml(server side) to 2.0.0 . Now i exported the application and dumped it to server (in 'release' folder where update.xml is there.).
Now when i launch the application , Update feature is not working. Its nothing happening.
CheckNow() method is called but doing nothing. Please help me.
I am working on AIR 3.1
Its not giving any kind of error. Only Updation Window is not working.
Please tell me what i m doing wrong. Thanks.
1) trace real version and from update.xml and be sure you did it correct. You must be sure you run old app with 1.0.0 version. BTW, you can use property ApplicationUpdaterUI.currentVersion instead of NativeApplication.nativeApplication.applicationDescriptor
2) You do not listen events after checkNow() calling. Listen all events, like in this article. In
http://thanksmister.com/2009/09/13/custom-air-updater-interface-using-applicationupdater/ (see UpdateManager.as#initialize()):
appUpdater.addEventListener(UpdateEvent.INITIALIZED, updaterEvent);
appUpdater.addEventListener(StatusUpdateEvent.UPDATE_STATUS, updaterEvent);
appUpdater.addEventListener(UpdateEvent.BEFORE_INSTALL, updaterEvent);
appUpdater.addEventListener(StatusUpdateErrorEvent.UPDATE_ERROR, updaterEvent);
appUpdater.addEventListener(UpdateEvent.DOWNLOAD_START, updaterEvent);
appUpdater.addEventListener(ProgressEvent.PROGRESS, updaterEvent);
appUpdater.addEventListener(UpdateEvent.DOWNLOAD_COMPLETE, updaterEvent);
appUpdater.addEventListener(DownloadErrorEvent.DOWNLOAD_ERROR, updaterEvent);
appUpdater.addEventListener(ErrorEvent.ERROR, updaterEvent);
private function updaterEvent(event : Event) : void {
trace(event.type);
}
possible, you missed some thing...
in your server side update.xml you should replace the tag version with versionNumber.
This changed since AIR 2.5.