In te below code I am trying to create a folder for persisted data. As you see I created private final String folder = "//temp"; and the object persistence. But the problem is when i run the App, i says Connection Failed, this message comes out from the client connection synchronous listener, and when I connect without the object persistence, every thing works fine.
Am i wrongly initializing the folder variable or using MqttClientPersistence persistence incorrectly?
code:
private final String folder = "//temp";
private final int keepAliveInterval = 30;
private final String TAG = this.getClass().getSimpleName();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mqtt_proj_01_layout);
final MqttClientPersistence persistence = new MqttDefaultFilePersistence(folder);
final MqttAndroidClient client2 = new MqttAndroidClient(getApplicationContext(), serverURI, clientID,persistence);
Do you have filesystem read/write permissions enabled for your android application?
Also the path probably wants to be application specific directory. You can get the application specific directory using something like this:
File outputDir = context.getCacheDir();
Related
Looking at this issue xamarin/Essentials#1322, how do I download a file on both Android ( versions 6-10, Api 23-29 ) and iOS ( version 13.1+ ) that is publicly available (share-able to other apps, such as Microsoft Word). I don't need to give write access to the other apps, just read-only is ok if it must be restricted.
I get the following exception:
[Bug] Android.OS.FileUriExposedException: file:///data/user/0/{AppBundleName}/cache/file.doc exposed beyond app through Intent.getData()
With the following code.
public static string GetCacheDataPath( string fileName ) => Path.Combine(Xamarin.Essentials.FileSystem.CacheDirectory, fileName);
public static FileInfo SaveFile( string filename, Uri link )
{
using var client = new WebClient();
string path = GetCacheDataPath(filename);
DebugTools.PrintMessage(path);
client.DownloadFile(link, path);
return new FileInfo(path);
}
public async Task Test(Uri link)
{
LocalFile path = await SaveFile("file.doc", link).ConfigureAwait(true);
var url = new Uri($"ms-word://{path.FullName}", UriKind.Absolute);
await Xamarin.Essentials.Launcher.OpenAsync(url).ConfigureAwait(true);
}
With this answer, I created a FileService interface and it works with local private files but I am unable to share the files. Starting with Android Q (10 / Api 29), the following is deprecated.
string path = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads).AbsolutePath; // deprecated
I get the following exception:
System.UnauthorizedAccessException: Access to the path '/storage/emulated/0/Download/file.doc' is denied. ---> System.IO.IOException: Permission denied
I haven't found any way yet to get a public path for Android 10 with Xamarin.Forms. I've looked at the Android Docs for Content providers but it's in Java, and I can't get it working in C# yet.
Any help would be greatly appreciated.
I did find a Solution
Found a fix
For Android
public Task<System.IO.FileInfo> DownloadFile( Uri link, string fileName )
{
if ( link is null )
throw new ArgumentNullException(nameof(link));
using System.Net.WebClient client = new System.Net.WebClient();
// MainActivity is the class that loads the application.
// MainActivity.Instance is a property that you set "Instance = this;" inside of OnCreate.
Java.IO.File root = MainActivity.Instance.GetExternalFilesDir(MediaStore.Downloads.ContentType);
string path = Path.Combine(root.AbsolutePath, fileName);
client.DownloadFile(link, path);
return Task.FromResult(new System.IO.FileInfo(path));
}
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
internal static MainActivity Instance { get; private set; }
protected override void OnCreate(Bundle savedInstanceState)
{
...
Instance = this;
...
}
...
}
For iOS
public Task<System.IO.FileInfo> DownloadFile( Uri link, string fileName )
{
if ( link is null )
throw new ArgumentNullException(nameof(link));
using System.Net.WebClient client = new System.Net.WebClient();
string path = Path.Combine(Xamarin.Essentials.FileSystem.CacheDirectory, fileName)
client.DownloadFile(link, path);
return Task.FromResult(new System.IO.FileInfo(path));
}
public async Task Share()
{
// back in shared project, choose a file name and pass the link.
System.IO.FileInfo info = await DependencyService.Get<IDownload>().DownloadFile(new Uri("<enter site>", "file.doc").ConfigureAwait(true);
ShareFile shareFile = new ShareFile(info.FullName, "doc"); // enter the file type / extension.
var request = new ShareFileRequest("Choose the App to open the file", shareFile);
await Xamarin.Essentials.Share.RequestAsync(request).ConfigureAwait(true);
}
Note that for iOS, due to Apple's infinite wisdom... I cannot share the file directly with another app as I can on Android. Sandboxing is good for security but in this case, how they implemented it, it limits options. Both Applications must be pre-registered / pre-allocated in an "App Group" to share files directly. See this Article and the Apple Docs for more information.
I need to set some configurations for my android application, like the server IP and Port, which the application should use to communicate. How can I add do it?
I thought of using sharedpreferences. How can I push a sharedpreference file with configurations, before even installing the app.
P.S. I don't want to hardcode the IP and PORT in the code base.
Any suggestions would be helpful.
Thanks
It's not possible. An alternate would be to ship the app with a json config file - put that in the assets folder
And read the contents of the file to prefill values you need.
try {
InputStream inputStream = mContext.getAssets().open(mContext.getPackageName() + ".json");
int size = inputStream.available();
byte[] buffer = new byte[size];
if (inputStream.read(buffer) != size) {
throw new IndexOutOfBoundsException();
}
inputStream.close();
JSONObject jsonObject = new JSONObject(new String(buffer, StandardCharsets.UTF_8));
server_ip = jsonObject.getString("server_ip");
server_port = jsonObject.getString("server_port");
} catch (Exception e) {
e.printStackTrace();
}
}
I would personally put that code in a class that extends Application, so it is called only once.
You CAN NOT push anything before installing. But you can store necessary data in SharedPreferences and retrieve whenever you want. To write/read String data to/from SharedPreferences;
private static final String SHARED_PREFS = "sharedPrefYourApp";
private static final String TEXT = "IP/PORT";
private static final String KEY = "IP_PORT_KEY";
public static void saveData(Context context) {
SharedPreferences sharedPreferences =
context.getSharedPreferences(SHARED_PREFS, MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(KEY, TEXT);
editor.apply();
}
public static String loadData(Context context) {
SharedPreferences sharedPreferences =
context.getSharedPreferences(SHARED_PREFS, MODE_PRIVATE);
String text = sharedPreferences.getString(KEY, "");
return text;
}
Another approach would be using something like Firebase Remote Config and then providing the app with configurations dynamically. Think of this approach as the app connecting to a static place; downloads configurations securely and then reacts to those configurations and connects to the end services securely.
If you are trying to make this secure, I would recommend using the Android keychain instead of SharedPreferences - I barely use SharedPreferences (although that's a preference or it depends on security and use case needs).
This is now available in Android via "Managed Configurations"
https://developer.android.com/work/managed-configurations
I am building an app based on the mobile hub sample app. The sample-app has the API keys stored in a class file AWSconfiguration:
public class AWSConfiguration {
// AWS MobileHub user agent string
public static final String AWS_MOBILEHUB_USER_AGENT =
"MobileHub ********* aws-my-sample-app-android-v0.16";
// AMAZON COGNITO
public static final Regions AMAZON_COGNITO_REGION =
Regions.fromName("us-east-1");
public static String AMAZON_COGNITO_IDENTITY_POOL_ID = "us-east-************6";
// Google Client ID for Web application
public static String GOOGLE_CLIENT_ID ="";//"*********************.apps.googleusercontent.com";
public static final Regions AMAZON_DYNAMODB_REGION =
Regions.fromName("us-east-1");
public static String AMAZON_COGNITO_USER_POOL_ID = "************";
public static String AMAZON_COGNITO_USER_POOL_CLIENT_ID = "*************";
public static String AMAZON_COGNITO_USER_POOL_CLIENT_SECRET = "*************";
private static final AWSMobileHelperConfiguration helperConfiguration = new AWSMobileHelperConfiguration.Builder()
.withCognitoRegion(AMAZON_COGNITO_REGION)
.withCognitoIdentityPoolId(AMAZON_COGNITO_IDENTITY_POOL_ID)
.withCognitoUserPool(AMAZON_COGNITO_USER_POOL_ID,
AMAZON_COGNITO_USER_POOL_CLIENT_ID, AMAZON_COGNITO_USER_POOL_CLIENT_SECRET)
.build();
/**
* #return the configuration for AWSKit.
*/
public static AWSMobileHelperConfiguration getAWSMobileHelperConfiguration() {
return helperConfiguration;
}
}
It seems unsafe to store the client secret key this way. What are the risks?
I experiemnted with hiding the keys in JNI files but could not find the proper entry point in the activity to set the keys before they are called from the mobile helper.
Storing in clear text is generally a bad idea, as you guessed. You could use the android keystore, store it encrypted (the stronger the key, the better), obfuscate it with some unique identifier of your device, or access it via some API you control and secure. It's possible to use some other solution, or a combination of the above possibilities. The final decision comes down to you and what your app needs/abilities are, but there's a few ways to hide it.
SharedPreferences.Editor can be a solution.
Password or something like this are stored in SharedPreferences.
In my game, there is a highscore that is supposed to be saved.
private void saveHighscore()
{
FileHandle file = Gdx.files.local("asdwdasfwad/asdawwafs.txt");
String highscoreString = Integer.toString(this.highscore);
file.writeString(highscoreString, false);
Gdx.app.log("saving", "saving");
}
private int loadHighscore()
{
FileHandle file = Gdx.files.local("asdwdasfwad/asdawwafs.txt");
String highscoreString = file.readString();
int highscore = Integer.parseInt(highscoreString);
Gdx.app.log("loading", "loading");
return highscore;
}
When I run this on my phone, "saving" is logged into the console without any errors, even though the path I have specified (asdwdasfwad/asdawwafs.txt) doesn't even exist. Even if I use an existent path, no file is created.
Not a direct answer to your problem, but since you just want to save a highscore:
You can just use Preferences for this. Its an class provided by libGDX that allows you to easily save small data.
For further information visit the official documentation page:
Click here
After I followed all the steps for the push notification sample app. I wasn't able to send a notifaction to myself. I could send a pushmessage from my PC to my phone, but when I use the button Send myself a Notification nothing happens.
I am using Android sdk
After starting the app I do see that my Device is Registerd
Here is my settings.java
package com.ganyo.pushtest;
/** Change these values to match your setup! */
public class Settings {
static final String UNASSIGNED_ORG_VALUE = "";
// Google Client Id from Google API Console
static final String GCM_SENDER_ID = "xxxxxxxxxxxxx";
// Notifier Name in App Services
static final String NOTIFIER = "androidDev";
static final String API_URL = "https://api.usergrid.com";
static final String ORG = "xxxxxxx";
static final String APP = "sandbox";
// set these if you want to use a user login
static final String USER = null;
static final String PASSWORD = null;
}
I'm not sure what the UNASSIGNED_ORG_VALUE should be.
Thx in advance.
No need to assign any value to UNASSIGNED_ORG_VALUE. It's only used to check that you've entered the other values.
Please check your Android logs as well as the Apigee Console to see what error messages might have been generated during your push attempt. This will help you debug the issue.
Finally, you could try providing your notifier name here in all lowercase. (Note: This shouldn't generally be necessary, but I've heard there may be a issue that affects notifier name resolution.)