In my application(which is run on a couple of type of tv-boxes from same manufacturer and all deviecs are rooted) I do check last version of my application from a server of my own and if there is a new version available, I download the APK file and try to install it using pm install command.
Though I've never started any activity to install the APK, it automatically opens the activity from intent Intent intent = new Intent(Intent.ACTION_VIEW);.
I've never used Intent.ACTION_VIEW in my whole project, so I assume that the android DownloadManager opens this activity automatically, and I want to stop it from doing so. Any idea how?
Here is my code:
//this is the method where I download the apk
//I'm putting only the important parts to make the question as short as possible
private void downloadApk() {
if (status == State.DOWNLOADING)
return;
setStatus(State.DOWNLOADING);
String destination = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/";
String fileName = appType + versionInfo.getVersionName() + ".apk";
destination += fileName;
Uri uri = Uri.parse("file://" + destination);
String url = versionInfo.getUrl();
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setDescription("Downloading new Hub version");
request.setTitle("Downloading");
request.setDestinationUri(uri);
downloadId = manager.enqueue(request);
monitorDownloadPercentage();
mContext.registerReceiver(onComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
}
This method only calculates how much of the file is downloaded in percentage and calls the callback with the value. This calculated value, on it's path updates one ProgressBar and gets set as the text of a TextView.
private void monitorDownloadPercentage() {
new Thread(new Runnable() {
#Override
public void run() {
downloading = true;
while (downloading) {
DownloadManager.Query q = new DownloadManager.Query();
q.setFilterById(downloadId);
Cursor cursor = manager.query(q);
cursor.moveToFirst();
int bytes_downloaded = cursor.getInt(cursor
.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
int bytes_total = cursor.getInt(cursor.
getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
if (upgradeStateListener != null) {
upgradeStateListener.onDownloading((float) bytes_downloaded / bytes_total * 100);
}
if (cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) == DownloadManager.STATUS_SUCCESSFUL) {
downloading = false;
}
}
}
}).start();
}
This is the broadcast receiver which gets fired when download is completed. And I'm sure this broadcast doesn't cause my problem since the only place in the code where could cause it is the onDownloadCompleted() method(which installs the APK using root privileges and I've commented all of it's body and it still pops up the activity.
BroadcastReceiver onComplete = new BroadcastReceiver() {
public void onReceive(Context ctxt, Intent intent) {
long downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0);
final Cursor cursor = manager.query( new DownloadManager.Query().setFilterById(downloadId));
if (cursor.moveToFirst()) {
Uri uri ;
final String downloadedTo = cursor.getString(
cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
uri = Uri.parse(downloadedTo);
Log.d(TAG, "onReceive: it's ok " + uri.getPath());
upgradeStateListener.onDownloadCompleted(uri.getPath());
}
setStatus(State.DOWNLOADED);
mContext.unregisterReceiver(this);
}
};
Related
I have an android application which updates itself from a server, if new version of the apk is present.
The problem is, it is now not working on devices running android 10 and higher. The root cause of the problem is that ACTION_VIEW is deprecated, and no longer supported since android 10. Here is the legacy code which is not working:
private void installapk(File file) {
Log.w(TAG, "Installing new version...");
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
// Otherwise will throw exception, have to install it in new task
intent.setFlags(intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
So after searching for a while now, it seems the only option I have left is to use packagemanager class to do the job. I found this example, which compiles and runs without giving any error message, but it seems like it does nothing.
public static void install(Context context, String packageName, File file) {
InputStream in = null;
OutputStream out = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
PackageInstaller.Session session = null;
try {
in = new FileInputStream(file);
PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
//
params.setAppPackageName(packageName);
// set params
int sessionId = packageInstaller.createSession(params);
session = packageInstaller.openSession(sessionId);
out = session.openWrite(file.getName(), 0, -1);
final byte[] buffer = new byte[65536];
int bytes_read;
while((bytes_read = in.read(buffer)) != -1){
out.write(buffer, 0, bytes_read);
}
session.fsync(out);
in.close();
out.close();
session.commit(createIntentSender(context, sessionId, ACTION_INSTALL_COMPLETE));
} catch (IOException e) {
Log.w(TAG, e.getMessage());
} finally {
;
}
}
}
private static IntentSender createIntentSender(Context context, int sessionId, String action) {
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, sessionId, new Intent(action), 0);
return pendingIntent.getIntentSender();
}
This code runs fine, but as I said, nothing happens as a result. What am I doing wrong?
I have a video that plays on my application from a server. I want to make it possible to share the video to WhatsApp when the shared button is clicked.
I understand that I have to download load the video to the device storage before sharing through Intent. Below is my code, when the shared button is click the progress bar keeps loading and nothing else happens, I'm also using FileProvider. How do I go about this?
The getVideoUrl() method below takes in the video url and returns the Uri (file path) after the video has been downloaded
private Uri getVideoUrl(String fileURL) {
Uri videoUri = null;
try {
File rootFile = new File(getCacheDir(), "share_video_" + System.currentTimeMillis() + ".mp4");
URL url = new URL(fileURL);
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("GET");
c.connect();
FileOutputStream f = new FileOutputStream(rootFile);
InputStream in = c.getInputStream();
byte[] buffer = new byte[1024];
int len1 = 0;
while ((len1 = in.read(buffer)) > 0) {
f.write(buffer, 0, len1);
}
videoUri = FileProvider.getUriForFile(this,
getPackageName() + ".provider", rootFile);
f.close();
} catch (IOException e) {
Log.d("Error....", e.toString());
}
return videoUri; // returns the file path to the video from storage
}
method to share the video,which is called is onClick of the share button. Then I receive the resultCode in onActivityResult() and make the progressBar invisible and display a message that the video has been shared.
public void shareVideo(String videoUrl, String desc){
progressBar.setVisibility(View.VISIBLE);
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("*/*");
i.setPackage("com.whatsapp");
i.putExtra(Intent.EXTRA_TEXT, desc ); //to share text
i.putExtra(Intent.EXTRA_STREAM, getVideoUrl(videoUrl)); //to share video
i = Intent.createChooser(i, "Share video");
startActivityForResult(i, POSTED_VIDEO);
}
shareVideo() is now called in the shareButtonOnClickListner()
#Override
public void onClick(View v) {
String videoUrl = "https://linktoVideo.mp4"; //just an example link
String desc = "Shared Video;
switch (v.getId()) {
case R.id.post_image:
shareVideo(videoUrl, desc);
}
}
What am I doing wrongly?
I followed this simple app AndroidScannerDemo which has two main button open camera and open gallery. The Camera is working fine on my phone API 19, but when I try to launch the camera on other devices or emulator the app crash.
From what I could understand this could be due to permission
Edit : Apparently this was asked here awhile ago as well but the issue remain
Wrong Update : the root problem coming from createImageFile method
I tried changing
//cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, tempFileUri); to
cameraIntent.putExtra(ScanConstants.OPEN_INTENT_PREFERENCE, preference);
I'm able to start the camera but I get crash right after taking picture
Update 2 : I'm trying following this article provided the answer below the only issue I'm using fragment
So How do I change this line
tempFileUri = FileProvider.getUriForFile(getActivity().getApplicationContext(),
"com.scanlibrary.provider", // As defined in Manifest
file);
to
tempFileUri = FileProvider.getUriForFile(PickImageFragment.this,
getString(R.string.file_provider_authority),
file); inside a fragment !
Wrong First argument PickImageFragment
EDIT : Changed openCamera() method inside PickImageFragment
What Im I missing ?
Stack trace
2019-11-29 23:45:05.750 27993-27993/com.nabeeltech.capturedoc E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.nabeeltech.capturedoc, PID: 27993
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.nabeeltech.capturedoc/com.scanlibrary.ScanActivity}: java.lang.SecurityException: UID 10091 does not have permission to content://com.scanlibrary.provider/external_files/scanSample/IMG_20191129_224505.jpg [user 0]
Caused by: java.lang.SecurityException: UID 10091 does not have permission to content://com.scanlibrary.provider/external_files/scanSample/IMG_20191129_224505.jpg [user 0]
at com.scanlibrary.PickImageFragment.openCamera(PickImageFragment.java:131)
at com.scanlibrary.PickImageFragment.handleIntentPreference(PickImageFragment.java:79)
at com.scanlibrary.PickImageFragment.init(PickImageFragment.java:60)
at com.scanlibrary.PickImageFragment.onCreateView(PickImageFragment.java:50)
PickImageFragment
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.pick_image_fragment, null);
init();
return view;
}
private void init() {
cameraButton = (ImageButton) view.findViewById(R.id.cameraButton);
cameraButton.setOnClickListener(new CameraButtonClickListener());
galleryButton = (ImageButton)
view.findViewById(R.id.selectButton);
galleryButton.setOnClickListener(new GalleryClickListener());
if (isIntentPreferenceSet()) {
handleIntentPreference();
} else {
getActivity().finish();
}
}
private void handleIntentPreference() {
int preference = getIntentPreference();
if (preference == ScanConstants.OPEN_CAMERA) {
openCamera();
} else if (preference == ScanConstants.OPEN_MEDIA) {
openMediaContent();
}
}
public void openCamera() {
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
Uri tempFileUri = null;
File file = createImageFile();
boolean isDirectoryCreated = file.getParentFile().mkdirs();
Log.d("", "openCamera: isDirectoryCreated: " + isDirectoryCreated);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
tempFileUri = FileProvider.getUriForFile(getActivity().getApplicationContext(),
"com.scanlibrary.provider", // As defined in Manifest
file);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, tempFileUri);
} else {
tempFileUri = Uri.fromFile(file);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, tempFileUri);
}
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP) {
cameraIntent.setClipData(ClipData.newRawUri("", tempFileUri));
cameraIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
startActivityForResult(cameraIntent, ScanConstants.START_CAMERA_REQUEST_CODE);
private File createImageFile() {
clearTempImages();
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new
Date());
File file = new File(ScanConstants.IMAGE_PATH, "IMG_" + timeStamp +
".jpg");
fileUri = Uri.fromFile(file);
return file;
}
private void clearTempImages() {
try {
File tempFolder = new File(ScanConstants.IMAGE_PATH);
for (File f : tempFolder.listFiles())
f.delete();
} catch (Exception e) {
e.printStackTrace();
}
}
ScanConstants
public class ScanConstants {
public final static int PICKFILE_REQUEST_CODE = 1;
public final static int START_CAMERA_REQUEST_CODE = 2;
public final static String OPEN_INTENT_PREFERENCE = "selectContent";
public final static String IMAGE_BASE_PATH_EXTRA = "ImageBasePath";
public final static int OPEN_CAMERA = 4;
public final static int OPEN_MEDIA = 5;
public final static String SCANNED_RESULT = "scannedResult";
public final static String IMAGE_PATH = Environment
.getExternalStorageDirectory().getPath() + "/scanSample";
public final static String SELECTED_BITMAP = "selectedBitmap";
}
You've written the code Fileprovider.getUriforFile which is fine, but have you declare the permissions required.
The only way to solve this is to grant permissions to all of the packages that might need it, like this:
List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
If above doesn't solve issue i'd suggest to refer this article by Lorenzo Quiroli that solves this issue for older Android versions.
He discovered that you need to manually set the ClipData of the Intent and set the permissions for it, like so:
if ( Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP ) {
takePictureIntent.setClipData( ClipData.newRawUri( "", photoURI ) );
takePictureIntent.addFlags( Intent.FLAG_GRANT_WRITE_URI_PERMISSION|Intent.FLAG_GRANT_READ_URI_PERMISSION );
}
I try, to check if this file exists after i downloaded it, but its says to me that is doesnt exist
#Override
public void handleResult(Result result)
{
myResult = result;
dm = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
Uri uri = Uri.parse(result.getText());
DownloadManager.Request request = new DownloadManager.Request(uri);
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
String nameOfFile = URLUtil.guessFileName(result.getText(),null, MimeTypeMap.getFileExtensionFromUrl(result.getText()));
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, nameOfFile);
dm.enqueue(request);
String erg = "";
File mPath = new File((Environment.DIRECTORY_DOWNLOADS + "/" + nameOfFile));
if (mPath.getAbsoluteFile().exists()) {
erg = "existiert";
}else
{
erg = "existiert nicht";
}
}
The downloading process is happening on background. So after enqueue() your file doesn't exist cause it's not downloaded yet.
You just need to register BroadcastReceiver with this
ACTION_DOWNLOAD_COMPLETE intent filter. And DownloadManager will broadcast when downloads complete. See documentation here: https://developer.android.com/reference/android/app/DownloadManager.html#ACTION_DOWNLOAD_COMPLETE
I am using a DownloadManager to handle a download in my application, I would like to notify the user when the download is completed.
I am using the folowing code that is working well
public void downloaddownload(View v){
View v2 = (View) v.getParent();
TextView urlView = (TextView) v2.findViewById(R.id.url);
String urlString = (String) urlView.getText().toString();
TextView artistView2 = (TextView) v2.findViewById(R.id.artist);
final String artistString = (String) artistView2.getText().toString();
TextView titleView2 = (TextView) v2.findViewById(R.id.title);
final String titleString = (String) titleView2.getText().toString();
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(urlString));
request.setDescription(titleString);
request.setTitle(artistString);
// in order for this if to run, you must use the android 3.2 to compile your app
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
}
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_MUSIC + "/folder", titleString + " - " + artistString + ".mp3");
Toast.makeText(mainContext, "Downloading " + titleString + " - " + artistString, Toast.LENGTH_SHORT).show();
// get download service and enqueue file
DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);
onComplete = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Toast.makeText(mainContext, "Download \" " + titleString + " - " + artistString + "\" completed", Toast.LENGTH_LONG).show();
}
};
registerReceiver(onComplete, new IntentFilter(
DownloadManager.ACTION_DOWNLOAD_COMPLETE));
}
The problem is that the onReceive method is called for previous downloads too.
Let's say I download a.mp3, b.mp3 and c.mp3, when a.mp3 is completed I recieve a.mp3 completed, when b.mp3 is completed I recieve a.mp3 is completed, then a new toast b.mp3 is completed...
How could I prevent this? thank you.
Yo are registering a BroadcastReceiver each time you download a file. That means, the second time you download a file, you'll have two receivers registered. You should probably unregister them using unregisterReceiver() after the work is done (probably in onReceive()).
long downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0);
Query query = new Query();
query.setFilterById(downloadId);
Cursor cur = manager.query(query);
if (cur.moveToFirst()) {
int columnIndex = cur.getColumnIndex(DownloadManager.COLUMN_STATUS);
if (DownloadManager.STATUS_SUCCESSFUL == cur.getInt(columnIndex)) {
titleString=cur.getString(cur.getColumnIndex(DownloadManager.COLUMN_TITLE));}
you should use this to get your title string for individual downloads . because every download have their own id . I know Answer is too late but it may help for others in future...