I have two issues
1) Annotation not work
2) I want read online pdf document
I build MuPDF using this link
http://mupdf.com/doc/how-to-build-mupdf-for-android
1) I get project mupdf/platform/android
I run that default app annotation working fine but i create that project to library file they asking want to save but annotation not save.
2) It will read local file but i want read online file also. my android code
pdf = (TextView) findViewById(R.id.pdf);
onPdf = (TextView) findViewById(R.id.onPdf);
pdf.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Uri uri = Uri.parse(path);
Intent intent = new Intent(context, MuPDFActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.setData(uri);
startActivity(intent);
}
});
onPdf.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Uri uri = Uri.parse(netPath);
Intent intent = new Intent(context, MuPDFActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.setData(uri);
startActivity(intent);
}
});
Modified MuPDFActivity - I change all file are open openBuffer method
private MuPDFCore openBuffer(byte buffer[]) {
System.out.println("Trying to open byte buffer");
try {
String magic = "";
core = new MuPDFCore(this, buffer, magic);
// New file: drop the old outline data
OutlineActivityData.set(null);
} catch (Exception e) {
System.out.println(e);
return null;
}
return core;
}
My mainfest
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".PdfTestActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.artifex.mupdfdemo.MuPDFActivity"
android:label="#string/app_name"
android:theme="#style/AppBaseTheme" >
</activity>
<activity
android:name="com.artifex.mupdfdemo.OutlineActivity"
android:label="#string/outline_title"
android:theme="#android:style/Theme.Light.NoTitleBar.Fullscreen" >
</activity>
<activity
android:name="com.artifex.mupdfdemo.PrintDialogActivity"
android:label="#string/print"
android:theme="#android:style/Theme.Light.NoTitleBar.Fullscreen" >
</activity>
</application>
My stacktrace
12-25 19:02:41.544: E/TAG(22925): /sample.pdf: open failed: ENOENT (No such file or directory)
12-25 19:02:41.544: E/tag(22925): file
12-25 19:02:41.546: I/System.out(22925): Trying to open /sample.pdf
12-25 19:02:41.549: D/dalvikvm(22925): Trying to load lib /mnt/asec/com.example.testnew-1/lib/libmupdf.so 0x41792e10
12-25 19:02:41.576: D/dalvikvm(22925): Added shared lib /mnt/asec/com.example.testnew-1/lib/libmupdf.so 0x41792e10
12-25 19:02:41.576: D/dalvikvm(22925): No JNI_OnLoad found in /mnt/asec/com.example.testnew-1/lib/libmupdf.so 0x41792e10, skipping init
12-25 19:02:41.577: I/libmupdf(22925): Opening document...
12-25 19:02:41.577: E/libmupdf(22925): error: cannot open /sample.pdf: No such file or directory
12-25 19:02:41.577: E/libmupdf(22925): error: cannot load document '/sample.pdf'
12-25 19:02:41.577: E/libmupdf(22925): error: Cannot open document: '/sample.pdf'
12-25 19:02:41.577: E/libmupdf(22925): Failed: Cannot open document: '/sample.pdf'
12-25 19:02:41.580: I/System.out(22925): java.lang.Exception: Cannot open file: /sample.pdf
I download PDF file and convert to Bytes and open in MuPDF Core.
Download file
byte buffer[] = null;
HttpURLConnection urlConnection = null;
try {
urlConnection = (HttpURLConnection) urls[0].openConnection();
urlConnection.setReadTimeout(TIMEOUT_READ);
InputStream inputStream = urlConnection.getInputStream();
int len = inputStream.available();
buffer = new byte[len];
// inputStream.read(buffer, 0, len);
while ((len = inputStream.read(buffer)) != -1) {
byteBuffer.write(buffer, 0, len);
}
Log.e("Length", "" + byteBuffer.size());
inputStream.close();
// and then we can return your byte array.
} catch (Exception e) {
e.printStackTrace();
});
Open core
RelativeLayout mupdfWrapper;
MuPDFCore core;
public byte[] buffer;
mupdfWrapper = (RelativeLayout) viewRoot
.findViewById(R.id.mupdf_wrapper);
// Intent intent =new Intent(getActivity(),MuPDFActivity.class);
// startActivity(intent);
core = new MuPDFCore(getActivity(), buffer, "");
MuPDFReaderView mDocView = new MuPDFReaderView(getActivity());
mDocView.setAdapter(new MuPDFPageAdapter(getActivity(), core));
mupdfWrapper.addView(mDocView, new LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
Related
I'm trying to send an image that has been created using Canvas. there is no problem with the image and it can be displayed in ImageView.
this is the error I get:
D/onFailure: /storage/emulated/0/sign_3.png: open failed: ENOENT (No such file or directory)
I got the error even though I got permission to write and read external storage. where I found out from the following logs:
V/ContentValues: Permission is granted
this is my code to send the image:
mainactivityBinding.ivSignature.invalidate();
BitmapDrawable drawable = (BitmapDrawable)
mainactivityBinding.ivSignature.getDrawable();
Bitmap bitmap = drawable.getBitmap();
if(isStoragePermissionGranted()){
String root = Environment.getExternalStorageDirectory().toString();
File myDir = new File(root);
myDir.mkdirs();
String fname = "sign_"+ Preferences.getKeyIdLogin(getBaseContext()) +".png";
file = new File (myDir, fname);
if (file.exists ()) file.delete ();
try {
FileOutputStream out = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
};
}
String token = Preferences.getKeyTokenLogin(getBaseContext());
RequestBody postToken = RequestBody.create(okhttp3.MultipartBody.FORM, token);
RequestBody reqFile = RequestBody.create(MediaType.parse("image/*"), file);
MultipartBody.Partbody = MultipartBody.Part.createFormData("upload", file.getName(), reqFile);
Call<ResponseSign> call =getApi().postSign(postToken, body);
call.enqueue(new Callback<ResponseSign>() {
#Override
public void onResponse(Call<ResponseSign> call, Response<ResponseSign> response) {
String message = response.body().getMessage();
Log.d("RESPON", "onResponse: " + message);
}
#Override
public void onFailure(Call<ResponseEditProfil> call, Throwable t) {
String message = response.body().getMessage();
Log.d("RESPON", "onResponse: " + message);
}
});
this is the code for the given storage permission:
public boolean isStoragePermissionGranted() {
if (Build.VERSION.SDK_INT >= 23) {
if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED &&
checkSelfPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
Log.v(TAG,"Permission is granted");
return true;
} else {
Log.v(TAG,"Permission is revoked");
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, android.Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
return false;
}
}
else { //permission is automatically granted on sdk<23 upon installation
Log.v(TAG,"Permission is granted");
return true;
}
}
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
Log.v(TAG,"Permission: "+permissions[0]+ "was "+grantResults[0]);
//resume tasks needing this permission
}
}
this is my Retrofit Interface:
#Multipart
#POST("postSign")
Call<ResponseSign> postSign(#Part("token") RequestBody token,
#Part MultipartBody.Part image);
this is the contents of my manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.myapps">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:requestLegacyExternalStorage="true"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/Theme.MyTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
I tried this app on android 11.
what should I do so that the image can be sent to the server? does the image really have to be saved in a file? or it can be sent directly without having to save to a file?
it's my first time trying to upload an image to the server, so sorry if there are some basics I don't understand.
Thank you.
I know where I went wrong, I should have checked whether my device has external memory or not.
If the device does not have external memory, the file will be stored on the internal memory.
if you want to know how to check internal and external memory you can see here:
how to check internal and external storage if exist
I have some issues developing the nfc plugin in Unity.
When nfc tagging, onNewIntent is not called, the app moves to the background and the default nfc tagviewer opens.
It worked on Android 9 and below, but not android 10.
I found a suspicious logs using Logcat.
NfcService: setForegroundDispatch: Caller not in foreground.
2020-04-05 15:33:45.857 32411-32411/? E/class com.package.product.NFCPlugin: call Activity
2020-04-05 15:33:45.857 32411-32411/? E/class com.package.product.NFCPlugin: intent:Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp= com.package.product/.NFCPlugin bnds=[1131,670][1405,1123] (has extras) }
2020-04-05 15:33:45.857 32411-32411/? E/class com.package.product.NFCPlugin: Skill Launcher Cat: LAUNCHER
2020-04-05 15:33:45.858 32411-32411/? E/class com.package.product.NFCPlugin: onResume Activity
2020-04-05 15:33:45.858 32411-32411/? E/class com.package.product.NFCPlugin: Call Check ForegroundDispatch
2020-04-05 15:33:45.858 7904-7904/? I/[LGHome6]Launcher: rebindModel: rebind = false, flag = 0, currentPage = 4, isLandscape = true, mChangedProfileByMultiWindow = false, mOrientationOfCurrentLayout = 0, mWorkspaceLoading = false, mChangedProfile = true, mIsMirrorMode = false
2020-04-05 15:33:45.858 7506-7542/? E/NfcService: setForegroundDispatch: Caller not in foreground.
2020-04-05 15:33:45.859 32411-32735/? W/System.err: javax.net.ssl.SSLException: Write error: ssl=0x7c105b0c48: I/O error during system call, Broken pipe
2020-04-05 15:33:45.859 32411-32411/? E/class com.package.product.NFCPlugin: Equal Result:true
2020-04-05 15:33:45.860 2437-2462/? V/DesktopModeManager: Notification is not exist.
2020-04-05 15:33:45.860 5005-5005/? I/OpaLayout: Setting opa enabled to true
2020-04-05 15:33:45.863 2437-8224/? D/InputDispatcher: Window went away: Window{f88f9f9 u0 com.lge.launcher3/com.lge.launcher3.LauncherExtension}
I have found several places to print the log. As a result, I found the part that outputs the same log in Android Code Search.
https://cs.android.com/android/platform/superproject/+/master:packages/apps/Nfc/src/com/android/nfc/NfcService.java;l=1053;bpv=1;bpt=1?q=NfcService:%20setForegroundDispatch%20enable&ss=android%2Fplatform%2Fsuperproject
#Override
public void setForegroundDispatch(PendingIntent intent,
IntentFilter[] filters, TechListParcel techListsParcel)
{
NfcPermissions.enforceUserPermissions(mContext);
if (!mForegroundUtils.isInForeground(Binder.getCallingUid()))
{
Log.e(TAG, "setForegroundDispatch: Caller not in foreground.");
return;
}
// Short-cut the disable path
if (intent == null && filters == null && techListsParcel == null)
{
mNfcDispatcher.setForegroundDispatch(null, null, null);
return;
}
// Validate the IntentFilters
if (filters != null) {
if (filters.length == 0) {
filters = null;
} else {
for (IntentFilter filter : filters) {
if (filter == null) {
throw new IllegalArgumentException("null IntentFilter");
}
}
}
}
// Validate the tech lists
String[][] techLists = null;
if (techListsParcel != null) {
techLists = techListsParcel.getTechLists();
}
mNfcDispatcher.setForegroundDispatch(intent, filters, techLists);
}
According to the code, if a log occurs, processing is stopped without returning any results. So it seems that the foregroundDispatch does not proceed and goes over.
I've been trying for a long time to solve this issue, but couldn't find the same case, so I think it's a new issue in android 10.
Does anyone know how to fix this?
Lastly, I'm sorry that it was difficult to understand because I used a translation site to write this question.
Below is the code for the plugin.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
m_instance=this;
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (mNfcAdapter == null)
{
finish();
return;
}
pendingIntent = PendingIntent.getActivity(NFCPlugin.this, 0, new Intent(NFCPlugin.this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
IntentFilter _ndfFilter = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
try
{
_ndfFilter.addDataType("*/*");
}
catch (MalformedMimeTypeException e) {
throw new RuntimeException("Filter fail:", e);
}
IntentFilter _ndfFilter_NDEF = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
try {
_ndfFilter_NDEF.addDataType("*/*");
}
catch (MalformedMimeTypeException e) {
throw new RuntimeException("Filter fail:", e);
}
IntentFilter _ndfFilter_Tech = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
try {
_ndfFilter_Tech.addDataType("*/*");
}
catch (MalformedMimeTypeException e) {
throw new RuntimeException("Filter fail:", e);
}
mIntentFilter = new IntentFilter[] { _ndfFilter ,_ndfFilter_NDEF,_ndfFilter_Tech};
techListsArray = new String[][] {
new String[] {NFCPlugin.class.getName()},
new String[] {TagTechnology.class.getName()},
new String[] {NfcA.class.getName()},
new String[] {NfcB.class.getName()},
new String[] {NfcV.class.getName()},
new String[] {IsoDep.class.getName()},
new String[] {Ndef.class.getName()},
new String[] {NdefFormatable.class.getName()}
};
}
#Override
public void onResume() {
super.onResume();
Log.e(NFCPlugin.class.toString(), "onResume Activity");
mNfcAdapter.enableForegroundDispatch(this, pendingIntent, mIntentFilter, techListsArray);
}
#Override
public void onPause() {
super.onPause();
Log.e(NFCPlugin.class.toString(), "onPause Activity");
mNfcAdapter.disableForegroundDispatch(this);
}
Next is the manifest content.
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.package.product"
xmlns:tools="http://schemas.android.com/tools"
android:installLocation="preferExternal">
<uses-permission android:name="android.permission.NFC" >
</uses-permission>
<uses-feature
android:name="android.hardware.nfc"
android:required="true" >
</uses-feature>
<supports-screens
android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:xlargeScreens="true"
android:anyDensity="true"/>
<application
android:theme="#style/UnityThemeSelector"
android:icon="#mipmap/app_icon"
android:label="#string/app_name"
android:networkSecurityConfig="#xml/network_security_config"
>
<activity android:name=".NFCPlugin"
android:launchMode="singleTask"
android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
android:screenOrientation="landscape"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TAG_DISCOVERED" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED"/>
</intent-filter>
<meta-data android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="#xml/tech_list" />
<meta-data android:name="unityplayer.UnityActivity" android:value="true" />
</activity>
</application>
</manifest>
lastly, it is the contents of techList required for TECH_DISCOVERED.
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<tech-list>
<tech>android.nfc.tech.IsoDep</tech>
<tech>android.nfc.tech.NfcA</tech>
<tech>android.nfc.tech.NfcB</tech>
<tech>android.nfc.tech.NfcF</tech>
<tech>android.nfc.tech.NfcV</tech>
<tech>android.nfc.tech.Ndef</tech>
<tech>android.nfc.tech.NdefFormatable</tech>
<tech>android.nfc.tech.MifareClassic</tech>
<tech>android.nfc.tech.MifareUltralight</tech>
</tech-list>
</resources>
I wonder if you can help, I'm trying to download an apk file as an update after version check.
it's downloading the apk but it will not open the apk file its printing an error when trying to open the apk
error :
E/UpdateAPP: Update error! file:///storage/emulated/0/download/update.apk exposed beyond app through Intent.getData()
code :
public class UpdateApp extends AsyncTask<String,Void,Void> {
private Context context;
public void setContext(Context contextf){
context = contextf;
}
#Override
protected Void doInBackground(String... arg0) {
try {
URL url = new URL(arg0[0]);
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("GET");
c.setDoOutput(true);
c.connect();
String PATH = Environment.getExternalStorageDirectory() + "/download/";
File file = new File(PATH);
file.mkdirs();
File outputFile = new File(file, "update.apk");
if(outputFile.exists()){
outputFile.delete();
}
FileOutputStream fos = new FileOutputStream(outputFile);
InputStream is = c.getInputStream();
byte[] buffer = new byte[1024];
int len1 = 0;
while ((len1 = is.read(buffer)) != -1) {
fos.write(buffer, 0, len1);
}
fos.close();
is.close();
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(Environment.getExternalStorageDirectory() + "/download/" + "/update.apk")), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // without this flag android returned a intent error!
context.startActivity(intent);
} catch (Exception e) {
Log.e("UpdateAPP", "Update error! " + e.getMessage());
}
return null;
}}
my targetSdkVersion :
targetSdkVersion 27
thanks for the help
Since API 26, 'REQUEST_INSTALL_PACKAGES' permission is necessary permission to achieve install apk file.
Make 'file_paths.xml' in res/xml folder to use FileProvider api
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="" path="/" />
</paths>
Declare FileProvider and permission in AndroidManifest.xml
Note. I used Androidx version of FileProvider, if you don't use androidx, make sure android.support.v4.content.FileProvider
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="{PACKAGE_NAME}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
Get 'Uri' by FileProvider api and request install permission
private fun openFile(file: File) {
val uri = if (Build.VERSION.SDK_INT >= 24) {
val authority = packageName + ".fileprovider"
FileProvider.getUriForFile(this, authority, file)
} else {
Uri.fromFile(file)
}
val myMime = MimeTypeMap.getSingleton()
val mimeType = myMime.getMimeTypeFromExtension(file.extension)
val intent = Intent(Intent.ACTION_VIEW).apply {
setDataAndType(uri, mimeType)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
if (Build.VERSION.SDK_INT >= 26 && !packageManager.canRequestPackageInstalls()) {
startActivity(
Intent(
Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
Uri.parse("package:$packageName")
)
)
} else {
intent.action = Intent.ACTION_VIEW
startActivity(intent)
}
}
Edit -> remove specific methods using in my project.
I need to share image and add "Save image" item in android share menu, I saw something like this in 9gag app, they have "Save" item in share menu, and share menu seems to be a bottom sheet. But how it can be achieved?
What I've done:
I added empty activity with intent filter in manifest which launches service and this service downloads image
<activity
android:name=".model.services.ShareActivity"
android:icon="#drawable/download_icon"
android:label="Save">
<intent-filter
android:label="Save"
android:icon="#drawable/download_icon">
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*"/>
</intent-filter>
</activity>
and now I have this icon in share menu and it works, but this icon also appear in share menu of other apps, I need to show it only in my app, how I can make it private or something?
Ok, I found a solution. First - we need activity which will handle image saving intent, this activity can launch service or something. I make it like this:
public class ShareActivity extends Activity {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle extras = getIntent().getExtras();
String url = extras.getString("url");
String name = extras.getString("name");
String description = extras.getString("description");
SaveImageService.downloadFile(url, name, description);
finish();
}
}
Where SaveImageService has static method which handle image saving to SD card
Second, we need to add some text in manifest:
<activity
android:name=".model.services.ShareActivity"
android:icon="#drawable/download_icon"
android:label="Save">
<intent-filter
android:label="Save"
android:icon="#drawable/download_icon">
<action android:name="com.my_app.random_text.SAVE_IMAGE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*"/>
</intent-filter>
</activity>
Here intent filter has custom action (this is important) this custom action is just some string, not app package or something (I'm using package name just because I like it).
Next, we need to add this activity to share menu list:
this will get bitmap Uri for sharing in other apps from ImageView
// Returns the URI path to the Bitmap displayed in specified ImageView
static public Uri getLocalBitmapUri(ImageView imageView) {
// Extract Bitmap from ImageView drawable
Drawable drawable = imageView.getDrawable();
Bitmap bmp = null;
if (drawable instanceof BitmapDrawable) {
bmp = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
} else {
return null;
}
// Store image to default external storage directory
Uri bmpUri = null;
try {
File file = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS), "share_image_" + System.currentTimeMillis() + ".png");
file.getParentFile().mkdirs();
FileOutputStream out = new FileOutputStream(file);
bmp.compress(Bitmap.CompressFormat.JPEG, 80, out);
out.close();
bmpUri = Uri.fromFile(file);
} catch (IOException e) {
e.printStackTrace();
}
return bmpUri;
}
And this will collect all apps that can share image plus our save image intent
public void shareExcludingApp(Context ctx, PhotoView snapImage) {
// Get access to the URI for the bitmap
Uri bmpUri = ShareTool.getLocalBitmapUri(snapImage);
if (bmpUri == null) return;
List<Intent> targetedShareIntents = new ArrayList<>();
//get all apps which can handle such intent
List<ResolveInfo> resInfo = ctx.getPackageManager().queryIntentActivities(createShareIntent(bmpUri), 0);
if (!resInfo.isEmpty()) {
for (ResolveInfo info : resInfo) {
Intent targetedShare = createShareIntent(bmpUri);
//add all apps excluding android system and ourselves
if (!info.activityInfo.packageName.equals(getContext().getPackageName())
&& !info.activityInfo.packageName.contains("com.android")) {
targetedShare.setPackage(info.activityInfo.packageName);
targetedShare.setClassName(
info.activityInfo.packageName,
info.activityInfo.name);
targetedShareIntents.add(targetedShare);
}
}
}
//our local save feature will appear in share menu, intent action SAVE_IMAGE in manifest
Intent targetedShare = new Intent("com.my_app.random_text.SAVE_IMAGE"); //this is that string from manifest!
targetedShare.putExtra(Intent.EXTRA_STREAM, bmpUri);
targetedShare.setType("image/*");
targetedShare.setPackage(getContext().getPackageName());
targetedShare.putExtra("url", iSnapViewPresenter.getSnapUrlForSave());
targetedShare.putExtra("name", iSnapViewPresenter.getSnapNameForSave());
targetedShare.putExtra("description", iSnapViewPresenter.getSnapDescriptionForSave());
targetedShareIntents.add(targetedShare);
//collect all this intents in one list
Intent chooserIntent = Intent.createChooser(targetedShareIntents.remove(0),
"Share Image");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS,
targetedShareIntents.toArray(new Parcelable[targetedShareIntents.size()]));
ctx.startActivity(chooserIntent);
}
I have an application that I want to export the current SQLite database to the Internal storage Downloads folder of the device. I am getting the below error.
java.io.FileNotFoundException:
/storage/emulated/0/Download/Pipe_Tally: open failed: EACCES
(Permission denied)
I have what I think are the correct permissions included in the AndroidManifest.xml file for reading and writing to storage.
Here is the AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.bigdaddy.pipelinepipetally">
<uses-feature android:name="android.hardware.camera2"
android:required="true"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
<application
android:allowBackup="true"
android:debuggable="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme"
tools:ignore="HardcodedDebugMode">
<activity
android:name=".MainActivity"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".TallyActivity2"
android:windowSoftInputMode="adjustResize">
</activity>
<activity
android:name=".JobAndDbActivity"
android:windowSoftInputMode="adjustResize">
</activity>
<activity
android:name=".ExistingTallyActivity"
android:windowSoftInputMode="adjustResize">
</activity>
<activity android:name=".ImageToFullscreen"
android:windowSoftInputMode="adjustResize">
</activity>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.bigdaddy.pipelinepipetally.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths">
</meta-data>
</provider>
</application>
</manifest>
Here is the method I am trying to use to create the file for the /Downloads/ folder within the internal device storage:
public void backUpDatabase() {
/* Open your local db as the input stream */
DBHelper anotherDbHelper = null;
try {
try {
anotherDbHelper = new DBHelper(ExistingTallyActivity.this);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
String path = null;
if (anotherDbHelper != null) {
path = String.valueOf(getApplicationContext().getDatabasePath(anotherDbHelper.getDatabaseName()));
Log.i(TAG, "backUpDatabase: 1" +path);
}
File dbFile = null;
if (path != null) {
dbFile = new File(path);
Log.i(TAG, "backUpDatabase: 2" + String.valueOf(dbFile));
}
FileInputStream fis = null;
try {
if (dbFile != null) {
fis = new FileInputStream(dbFile);
Log.i(TAG, "backUpDatabase: 3" + String.valueOf(dbFile));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
String outFileName = (Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath()+ "/Pipe_Tally/");
Log.i(TAG, "backUpDatabase: 4"+ outFileName);
// Open the empty db as the output stream
OutputStream outputStream = null;
//FileOutputStream outputStream = null;
try {
outputStream = new FileOutputStream(outFileName);
Log.i(TAG, "backUpDatabase: 5"+ outFileName);
Log.i(TAG, "backUpDatabase: 6"+ String.valueOf(outputStream));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// Transfer bytes from the input-file to the output-file
byte[] buffer = new byte[1024];
int length;
try {
if (fis != null) {
while ((length = fis.read(buffer)) > 0) {
try {
if (outputStream != null) {
outputStream.write(buffer, 0, length);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
//Close the streams
try {
if (outputStream != null) {
outputStream.flush();
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fis != null) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
Here is the method I am using to check for the correct permissions at Runtime:
private boolean checkSdCardPermissions() {
/* Checking here if we already have permissions*/
if (ActivityCompat.checkSelfPermission(ExistingTallyActivity.this,
Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(ExistingTallyActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { /* Added this for Write External Storage permissions */
/* Checking to see if we are equal to or at a higher version than Marshmallow */
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
/* System default popup box here to ask for permissions. */
ActivityCompat.requestPermissions(ExistingTallyActivity.this,
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
MY_PERMISSION_READ_EXTERNAL_STORAGE);
/* Added this today for trying to prompt for writing external storage*/
ActivityCompat.requestPermissions(ExistingTallyActivity.this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
MY_PERMISSION_WRITE_EXTERNAL_STORAGE);
}
return true;
} else {
return false;
}
}
This is the onOptionsItemSelected() method that has the switch in it to handle the menu and what is selected. The case:R.id.menu_save_and_export:
is where I am checking if checkSdCardPermissions() method is true, then calling the backUpDatabase() method.
#Override
public boolean onOptionsItemSelected(MenuItem menuItem) {
switch (menuItem.getItemId()) {
/*
When Tally New Pipe is selected from the Menu, it sends the user to the TallyActivity
page, all encapsulated in a runnable and passed to thread below.
*/
case R.id.menu_new_pipe:
class ToTallyActivityManager implements Runnable {
#Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Intent intentToTallyActivity =
new Intent(ExistingTallyActivity.this, TallyActivity2.class);
startActivity(intentToTallyActivity); /* Heading to TallyActivity.*/
} /* run method ends here. */
} /* Runnable ends here. */
/* This thread takes care of the call to go to TallyActivity page from here.*/
mThreadToTallyActivity = new Thread(new ToTallyActivityManager());
mThreadToTallyActivity.start();
break;
/* When selected, sends the user to the MainActivity page. */
case R.id.menu_to_main:
Intent intentToMainActivity =
new Intent(ExistingTallyActivity.this, MainActivity.class);
startActivity(intentToMainActivity); // Heading to MainActivity
break;
/* When chosen allows the option to save and export the current DB.*/
case R.id.menu_save_and_export:
if (checkSdCardPermissions()) {
try {
backUpDatabase();
} catch (/*IOException*/ SQLException e) {
e.printStackTrace();
}
}
break;
/* When chosen from the menu, this kills the app completely. Has popup embedded.*/
default:
AlertHelperDialog.displayExitAlertDialog(ExistingTallyActivity.this);
} /* switch/case ends here */
return super.onOptionsItemSelected(menuItem);
} /* onOptionsItemSelected method ends here. */
I really appreciate any help or advice with fixing this issue. Thank you.
You have a android:maxSdkVersion="18" set on the WRITE_EXTERNAL_STORAGE permission. If you are running on a phone with SDK > 18 the app will not have permission to WRITE_EXTERNAL_STORAGE. So I suggest you remove the android:maxSdkVersion attribute. Read the documentation.
android:maxSdkVersion : The highest API level at which this permission should be granted to your app. Setting this attribute is useful if the permission your app requires is no longer needed beginning at a certain API level.
Change it as follows,
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />