In my Android Studio, I would like to connect the Mqtt Android client to my laptop host (in the same machine). I make it similar to this guide
https://www.hivemq.com/blog/mqtt-client-library-enyclopedia-paho-android-service/
Then, I found that the Android 12 (API 32 in my case) may not support the org.eclipse.paho:org.eclipse.paho.android.service:1.1.1 dependency. So, I followed this solution below, by imported the serviceLibrary-release.aar library from github provided instead. (The problem appear in this link was the same of my case)
Android paho mqtt crashes Android 12 - Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE
After that, I ran into another error.
error: constructor MqttAndroidClient in class MqttAndroidClient cannot be applied to given types;
MqttAndroidClient client = new MqttAndroidClient(MainActivity.this, "tcp://10.0.2.2:1883", clientId);
^
required: Context,String,String,Ack
found: MainActivity,String,String
reason: actual and formal argument lists differ in length
So I'm not sure that the library from the solution above can be applied to my old code, or, do I need to modify some code?
Here is my code and the gradle file.
repositories
maven {
url "https://repo.eclipse.org/content/repositories/paho-releases/"
}
Dependencies
implementation files('libs/serviceLibrary-release.aar')
implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5'
Android Manifest (Added permission below)
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<service android:name="info.mqtt.android.service.MqttService"/>
Main Activity
import info.mqtt.android.service.MqttAndroidClient;
public class MainActivity extends AppCompatActivity {
private Button buttonConnect;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buttonConnect = findViewById(R.id.buttonConnect);
buttonConnect.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String clientId = MqttClient.generateClientId();
Toast.makeText(MainActivity.this, clientId, Toast.LENGTH_SHORT).show();
MqttAndroidClient client = new MqttAndroidClient(MainActivity.this, "tcp://10.0.2.2:1883", clientId);
try {
IMqttToken token = client.connect();
token.setActionCallback(new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
Log.d("Debug", "onSuccess");
Toast.makeText(MainActivity.this, "onSuccess", Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
Log.d("Debug", "onFailure");
Toast.makeText(MainActivity.this, "onFailure", Toast.LENGTH_SHORT).show();
exception.printStackTrace();
}
});
} catch (MqttException e) {
e.printStackTrace();
}
}
});
}
}
The error appear in this line (when the button is clicked)
MqttAndroidClient client = new MqttAndroidClient(MainActivity.this, "tcp://10.0.2.2:1883", clientId);
From the error message prompted above. I think that's because the constructor's parameter of this class require a type Ack also, but I have no idea on that.
From output you provided, it seems you only need to specify Ack as the last parameter of your constructor.
It's acknowledgment that you received a message. According to official description, there is two modes available.
First, MqttAndroidClient.Ack.AUTO_ACK, which acknowledge automatically as soon as you received a message.
And then you have MqttAndroidClient.Ack.MANUAL_ACK, which requires you to manually acknowledge by doing MqttAndroidClient.acknowledgeMessage(String)
You can test it simply by adding the auto for now, and if it's ok then you can manually acknowledge yourself with custom rules.
package com.example.cppinandroid;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import org.opencv.core.Mat;
import java.io.File;
import static org.opencv.imgcodecs.Imgcodecs.CV_LOAD_IMAGE_GRAYSCALE;
import org.opencv.imgcodecs.Imgcodecs;
public class MainActivity extends AppCompatActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = findViewById( R.id.sample_text );
Mat image;
image = Imgcodecs.imread( "/home/<myName>/a.png", CV_LOAD_IMAGE_GRAYSCALE);
if ( image.size().height > 0 )
{
tv.setText( "No datfa in image!");
}
else
{
tv.setText( "xxff " + image.size().height);
}
}
}
I am NOT using any drawable or external media. Image is present in home folder and can be opened by a normal opencv c++ program by giving the exact same path.
Someone here told me that native C++ NDK will not be able to read Linux paths. Alright. Here it is all Java.
When I execute this, it always goes in else statement and shows the height as 0.0.
I have removed the extra code.
What is the way to read a normal png from home folder in this program on Linux?
All Android devices or emulators don't have access to storages outside like your Linux storage partition, they have access to their internal storage or sdcard. In the case of the emulator, their internal storage is emulated using a file with a certain format that cannot be easily read. In an emulator or a device that has Developer options enabled, one could use the adb command found within the Android SDK platform-tools folder to transfer files into it as such:
adb push file.jpg /sdcard/file.jpg
After that, you'll need to change the path of the file your using in the code to match and also enable permissions to READ_EXTERNAL_STORAGE (here external means external to the application your running, but still internal to the device).
Someone here told me that native C++ NDK will not be able to read
Linux paths. Alright. Here it is all Java.
Looking at your question and the answer, for start this is the same problem, trying to access a file that is not part of the device/emulator internal storage. However the answer isn't entirely true, the C/C++ code can access files and directories of the internal storage as long as permission is granted to the application. I would suggest you first try fixing the problem using Java and then switch back to the code you had in your other question but with the corrected path. With Java, you'll be using the Java wrapper for the OpenCV APIs, hence you'll need to call OpenCVLoader.initDebug() to load the wrapper lib. When using pure NDK, you'll only need to load the compiled lib (System.loadLibrary(<libname>) you created with the native C/C++ code.
When you use Imgcodecs.imread(...), it will read the path on your machine, which running your application.
So, if you run the Java Application, it will run on your JVM within your computer, that mean it read the path like ~/home/... on your computer and that path exist, so it can get somethings.
But, Android App will run on DVM within Android Device, that mean when you read ~/home/.., it will take that path on Android Device, but it wasn't exist on Android Devices. So you can't get anything.
The best practice, you should use Imgcodecs.imread(...) with the External Storage Path like some guys suggest you above.
Sometime, you maybe can use Imgcodecs.imread(...) on /mtn/..,path of SD Card, but it isn't correct completely.
see this example code, maybe help you, i tested this code and work for me, and i can get width and height of any image.
1) first you need import OpenCVLibrary to your project: see this Link - Link
2) then you need set READ_EXTERNAL_STORAGE permision to your application:
plz add this command on your AndroidManifest.xml:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
3) you need file picker for get specific file i use ru.bartwell:exfilepicker: Link
implementation 'ru.bartwell:exfilepicker:2.1'
4) and at the end you just add this simple code to your MainActivity:
private static final int READ_STORAGE_PERMISSION_REQUEST_CODE = 1;
private static final int EX_FILE_PICKER_RESULT = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initLoadOpenCV();
if (!checkPermissionForReadExtertalStorage()) {
requestPermissionForReadExtertalStorage();
}
}
private void initLoadOpenCV() {
boolean isDebug = OpenCVLoader.initDebug();
if (isDebug) {
Log.i("init Opencv", "init openCV success!!");
} else {
Log.e("init Opencv", "init openCV failure!!");
}
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == EX_FILE_PICKER_RESULT) {
ExFilePickerResult result = ExFilePickerResult.getFromIntent(data);
if (result != null && result.getCount() > 0) {
// Here is object contains selected files names and path
Log.i("folderLocation", result.getPath() + result.getNames().get(0));
Mat srcMat1 = Imgcodecs.imread(result.getPath() + result.getNames().get(0));
if (srcMat1.empty()) {
return;
}
int width = srcMat1.width();
int height = srcMat1.height();
int type = srcMat1.type();
Log.i("width", srcMat1.width() + "");
Log.i("height", srcMat1.height() + "");
Log.i("type", srcMat1.type() + "");
}
}
}
public boolean checkPermissionForReadExtertalStorage() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
int result = getApplicationContext().checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE);
return result == PackageManager.PERMISSION_GRANTED;
}
return false;
}
public void requestPermissionForReadExtertalStorage() {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
READ_STORAGE_PERMISSION_REQUEST_CODE);
}
#Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case READ_STORAGE_PERMISSION_REQUEST_CODE:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.e("value", "Permission Granted, Now you can use local drive .");
ExFilePicker exFilePicker = new ExFilePicker();
exFilePicker.start(this, EX_FILE_PICKER_RESULT);
} else {
Log.e("value", "Permission Denied, You cannot use local drive .");
requestPermissionForReadExtertalStorage();
}
break;
}
}
The first problem mentioned in the comment below the question is that you must load the native library that implements the image loading. This can be done with the following code:
static {
// TODO: use OpenCVLoader.initAsync for a real application
if (!OpenCVLoader.initDebug()) {
Log.d(TAG, "Failed to initialize OpenCV.");
}
}
In a real application you would use the initAsync function such that the loading of the libraries does not block the main thread. In a simple example this does not matter.
Another problem is, that file IO on Android requires a permission if you want to access files in arbitrary directories. The permission must be declared in your manifest file. This can be done by adding the following two lines above the application tag in the manifest file.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
These permissions must be requested at runtime. To check if the permission was granted already the following code can be used:
if (ContextCompat.checkSelfPermission(this,
android.Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
// do something with the permission
}
If the permission is not available, it can be requested as follows:
ActivityCompat.requestPermissions(this, new String[]{
android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, RW_PERMISSION_REQUEST_CODE);
Note: we only request the write permission because they are grouped and if the user grants the write permission we automatically also obtain the read permission.
To handle the result the onRequestPermissionsResult callback in the activity class should be overwritten as seen in the full code example below. Because the permission system is quite complex take a look at the official documentation. For info on requesting permissions look here.
Finally to make the loading work the file path must be correct. The user-accessible memory locations depend on the phone manufacturer, therefore it is good to use the system methods provided by Android to find the correct path, for example getExternalStorageDirectory(). More information on the various storage locations can be found here.
Here the full code:
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.os.Environment.getExternalStorageDirectory;
public class MainActivity extends AppCompatActivity {
private static String TAG = "MainActivity";
private static final int RW_PERMISSION_REQUEST_CODE = 123;
static {
// TODO: use OpenCVLoader.initAsync for a real application
if (!OpenCVLoader.initDebug()) {
Log.d(TAG, "Failed to initialize OpenCV.");
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (ContextCompat.checkSelfPermission(
this, WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
permissionDenied();
} else {
permissionGranted();
}
}
private void permissionDenied() {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, WRITE_EXTERNAL_STORAGE)) {
new AlertDialog.Builder(this)
.setTitle("Read/Write permission required to read an image file.")
.setCancelable(false)
.setPositiveButton("Grant", (dialog, which) ->
ActivityCompat.requestPermissions(this, new String[]{
WRITE_EXTERNAL_STORAGE}, RW_PERMISSION_REQUEST_CODE))
.setNegativeButton("Deny", (dialog, which) -> {
Toast.makeText(this,
"App cannot work without permission.", Toast.LENGTH_LONG).show();
this.finish();
})
.create()
.show();
} else {
ActivityCompat.requestPermissions(
this, new String[]{WRITE_EXTERNAL_STORAGE}, RW_PERMISSION_REQUEST_CODE);
}
}
private void permissionGranted() {
String path = getExternalStorageDirectory().getAbsolutePath() + "/a.png";
Mat image = Imgcodecs.imread(path, Imgcodecs.IMREAD_GRAYSCALE);
if (image.empty()) {
Toast.makeText(this, "Failed image", Toast.LENGTH_LONG).show();
} else {
Size size = image.size();
Toast.makeText(this, "Loaded image " + size.height, Toast.LENGTH_LONG).show();
// the following code is only necessary to display the image in an ImageView
ImageView iv = findViewById(R.id.imageView);
Mat tmp = new Mat((int) size.height, (int) size.width, CvType.CV_8U, new Scalar(4));
try {
Imgproc.cvtColor(image, tmp, Imgproc.COLOR_GRAY2RGBA, 4);
Bitmap bmp = Bitmap.createBitmap(tmp.cols(), tmp.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(tmp, bmp);
iv.setImageBitmap(bmp);
} catch (CvException e) {
Log.d(TAG, e.getMessage());
Toast.makeText(this, "Couldn't convert image.", Toast.LENGTH_LONG).show();
}
}
}
#Override
public void onRequestPermissionsResult(
int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
if (requestCode == RW_PERMISSION_REQUEST_CODE) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
permissionGranted();
} else {
permissionDenied();
}
} else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
}
To make this code work add an ImageView with id imageView to your activity_main.xml layout file.
Before you read on further I would like to clarify a few things
From question it is not clear where the code is running? It seems that the user is running this android app on a virtual machine or emulator.
1.1 - From the question it seems that the file she wants to open is on the home directory of Linux machine and not in the emulator's storage or on an Android device - in this case Please note that android apps running on emulator can not access files from your computer.
---- so if you were trying to access file on your Linux pc from within android emulator or vm, please note that it is not possible. Instead copy and put the file in the android emulator or device on which your app will be running.
Please clarify question and let us know whether you have file on the emulator storage (or android device) or it is on your pc and code running on emulator.
If you have file on emulator or android device, please make sure you have right permissions declared in manifest and you have also requested user for permission to read storage before trying to read the image.
Update
Thank you for the response in comments.
How to put file on emulator?
To add a file to the emulated device, drag the file onto the emulator screen. The file is placed in the /sdcard/Download/ directory. You can view the file from Android Studio using the Device File Explorer, or find it from the device using the Downloads or Files app, depending on the device version
source https://developer.android.com/studio/run/emulator
For permissions related stuff you can refer easy to follow documentation on official website - https://developer.android.com/training/permissions/requesting
or check out this question - Android marshmallow request permission?
You can also check -- https://stackoverflow.com/a/30434631/9640177
Depreciation note
Please check https://stackoverflow.com/a/59812593/9640177
To avoid dealing with permissions and external directories, you can also transfer your file to your app's internal storage using android studio -- explore device storage.
Update 2
Refer this answer please - https://stackoverflow.com/a/5990806/9640177
If you want to access them in runtime and not push the files there you have to have a public IP on the internet in which you can access and expose the files on HTTP with something like Apache Tomcat or FTP and then access that from the emulator.
You can also access it locally without exposing it to the whole internet, by accessing from the Android emulator to the "localhost" IP. You'll have to search which IP this is.
So your use case necessitates accessing files from location on your pc, you can use something like tomcat to create a local http server.
I think i added my maps google api key to the wrong place. Please help me:
The relevant api key also updated on resources:
<string name="google_maps_key" templateMergeStrategy="preserve" translatable="false">AIzaSyAzSgqQEZ..........KMsLlN4</string>
#Override
protected String doInBackground(String... strings) {
String response;
try {
String address = strings[0];
HttpDataHandler http = new HttpDataHandler();
String url = String.format("http://maps.googleapis.com/maps/api/geocode/json?address=%s&key=AIzaSyAzSgqQEZS1K1...........KMsLlN4", address);
response = http.getHTTPData(url);
return response;
} catch (Exception ex) {
}
return null;
}
I don't get any results, i think my access still denied...
Where should i put it?
from the official documentation->
got to the gradle.properties file and add a value with name
GOOGLE_MAPS_API_KEY="your api key"
and rebuild project.After in your manifest file you will see api key.
P.S don`t forget about permissions
The solution is to enable Geocoding API in Google Api console and restrict to specific app!!!
The app used to run. However, just recently it began to stop working. The device is running Android Nutella. Below is the LogCat.
java.lang.SecurityException: Permission Denial: reading com.google.android.music.store.ConfigContentProvider uri content://com.google.android.music.ConfigContent/one-key/2/ExplicitRestrictedByParentControl from pid=2500, uid=10373 requires the provider be exported, or grantUriPermission()
The app crashes in the the following code snippet on the last line(contained in a SongParser method).
String[] projection2 = {MediaStore.Audio.Media.ARTIST};
Uri songUri=null;
try {
songUri = Uri.parse("content://com.google.android.music.MusicContent/audio");
} catch (NullPointerException e){
e.printStackTrace();
}
if (songUri!=null) {
CursorLoader cl2 = new CursorLoader(context,
songUri, projection2, null, null, null);
cursor = cl2.loadInBackground();
I grant the Uri permissions in the following method after asking for permissions through the runtime permissions methods.
private void startService() {
//start intent to RssService for feedback
intent = new Intent(getActivity(), SongService.class);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
getContext().grantUriPermission("xxx.xxx.xxx.SongService",Uri.parse("content://com.google.android.music.MusicContent/audio"),Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra(SongService.RECEIVER, resultReceiver);
getActivity().startService(intent);
}
Here is where SongService calls SongParser.
#Override
protected void onHandleIntent(Intent intent) {
List<String> eventItems= null;
if (haveNetworkConnection()) {
parser = new SongParser();
eventItems = parser.getAllArtists(getApplicationContext());
}
Bundle bundle = new Bundle();
bundle.putSerializable(ITEMS, (Serializable) eventItems);
ResultReceiver receiver = intent.getParcelableExtra(RECEIVER);
receiver.send(0, bundle);}}
I have contained the permissions in the manifest as well. Again, this exception seemingly happened on its own.
<permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
What's happening is that for some reason, the (exported) MusicContent provider will redirect to ConfigContentProvider, which is not exported.
It seems that the way to solve it is to open Google Play Music. If you haven't launched it in a while, it will redirect to com.google.android.music.store.ConfigContentProvider and trigger a SecurityException. It's kind of problematic but at least I can tell my users what to do. Let me know if you can come up with something better.
It might also be a good idea to file a bug.
You do not have access to that ContentProvider. It is not exported, and that app did not pass you a Uri that you can use to access it.
Since presumably the Uri is from an app that you did not write, apparently an update to that app changed this behavior.
I'm trying to integrate the Backup Service with my app. It's all working right up to the moment I make the call to:
BackupManager bm = new BackupManager(context);
bm.dataChanged();
I see the following in my Logcat:
W/BackupManagerServiceļ¹ dataChanged but no participant pkg='com.company.app' uid=10102
Here's my implementation of BackupAgentHelper https://gist.github.com/cloakedninjas/fe135cb04bf324e26b0c if it helps...
public class CordovaBackupAgentHelper extends BackupAgentHelper {
static final String FILE_NAME = "gameData.json";
static final String FILES_BACKUP_KEY = "data_file";
#Override
public void onCreate() {
FileBackupHelper helper = new FileBackupHelper(this, FILE_NAME);
addHelper(FILES_BACKUP_KEY, helper);
}
#Override
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
ParcelFileDescriptor newState) throws IOException {
Log.d(Backup.LOG_TAG, "Backup requested: " + oldState.toString() + " | " + newState.toString());
synchronized (Backup.sDataLock) {
Log.d(Backup.LOG_TAG, "Backup requested: " + data.toString());
super.onBackup(oldState, data, newState);
}
}
#Override
public void onRestore(BackupDataInput data, int appVersionCode,
ParcelFileDescriptor newState) throws IOException {
synchronized (Backup.sDataLock) {
Log.d(Backup.LOG_TAG, "Restore given: " + data.toString());
super.onRestore(data, appVersionCode, newState);
}
}
}
I never see my log entry Backup requested. I've checked my Android manifest has the following attribute android:backupAgent="CordovaBackupAgentHelper"
Can anyone shed any light on this?
Update
I've noticed if I change my Manifest to have android:backupAgent="FooBar" I see no errors about undeclared class FooBar, but still see dataChanged but no participant pkg when attempting a backup. So my guess is the error is related to not being able to find my backup agent.
A couple of suggestions:
In your manifest, I think the declaration of the backup agent should either use the full name or the "dot" short form:
android:backupAgent="com.example.project.CordovaBackupAgentHelper"
or
android:backupAgent=".CordovaBackupAgentHelper"
Also, you must have backup enabled in your manifest:
android:allowBackup="true"
Having said that, I doubt these changes with make the warning message go away. I have a backup agent, and am unable to create the warning message you see by changing these declaration to invalid values.
If you are using the Google backup transport, have you registered for the service as described in the Data Backup Guide and added the key to your manifest?
Have you tried to trigger a backup operation using the adb bmgr tool to see if your agent gets called despite the warning message?
So my issue was not putting the attributes onto the correct element :(
I had:
<manifest android:backupAgent="com.cloakedninjas.cordova.plugins.BackupAgentHelper" ...
Instead of:
<application android:backupAgent="com.cloakedninjas.cordova.plugins.BackupAgentHelper" ...