I have coded an update dialog in my app which checks a json file on my server to see if an update is available and then shows an update app dialog box. I know that I can send users to playstore by using Intent but I saw facebook and some other apps also show the same dialog with update button and upon pressing that button, the app starts downloading and installing instantly without going to playstore. However when I added a download AsyncTask to download and install my app, it shows parse error.
Here is my code to download and parse:
public class DownloadTask extends AsyncTask<String, Integer, String> {
#Override
protected String doInBackground(String... urls) {
String path = "/sdcard/MyApp.apk";
try {
URL url = new URL(urls[0]);
URLConnection connection = url.openConnection();
connection.connect();
int fileLength = connection.getContentLength();
// download the file
InputStream input = new BufferedInputStream(url.openStream());
OutputStream output = new FileOutputStream(path);
byte data[] = new byte[1024];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
total += count;
publishProgress((int) (total * 100 / fileLength));
output.write(data, 0, count);
}
output.flush();
output.close();
input.close();
} catch (Exception e) {
}
return path;
}
// begin the installation by opening the resulting file
#Override
protected void onPostExecute(String path) {
Intent i = new Intent();
i.setAction(Intent.ACTION_VIEW);
i.setDataAndType(Uri.fromFile(new File(path)), "application/vnd.android.package-archive" );
Log.d("Lofting", "About to install new .apk");
getContext().startActivity(i);
}
}
Can someone please tell me what am I doing wrong here?
Thanks in advance. :)
Related
I'm busy creating a soundboard app, and I'm struggling to figure the code so that a sound can be shared on email and whatsapp and other apps. I've done research and I tried to code it but with no luck. The audio file isn't even saving on the external storage so that I can share the sound from there. I have already written the permission uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE", and that all works. I think its my code on actually saving the audio to external storage.
public String copyFiletoExternalStorage(int resourceId, String resourceName){
String pathSDCard = Environment.getExternalStorageDirectory() + "/Soundboard/sounds/" + resourceName;
try{
InputStream in = getResources().openRawResource(resourceId);
FileOutputStream out = null;
out = new FileOutputStream(pathSDCard);
byte[] buff = new byte[1024];
int read = 0;
try {
while ((read = in.read(buff)) > 0) {
out.write(buff, 0, read);
}
} finally {
in.close();
out.close();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return pathSDCard;
}
As you can see from above I parse in R.raw.audio, and "audio.mp3" in the parameters of resourceId and resourceName respectively.
The code below shows how I share the audio once its been saved. But obviously because the audio isn't being saved then I cant share it.
share1 = findViewById(R.id.share1);
share1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String sharePath = copyFiletoExternalStorage(R.raw.audio, "audio.mp3");
Uri uri = Uri.parse(sharePath);
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("audio/*");
share.putExtra(Intent.EXTRA_STREAM, uri);
startActivity(Intent.createChooser(share, "Share Sound"));
}
});
If you have any idea why its not working please help me. I have tried so many different ways but nothing seems to work.
I am working on an app for my company's internal use which will collect performance stats from network and post them on our Grafana server.
The app works fine with this context, but there is a problem:
App will run on a phone at a datacenter and it will be very difficult to access it if we need to update the app for adding features.
Also the phone will not have internet access. So I won't be able to update the app manually , or using Google Play.
I thought of writing a function to check a static URL and when we put an updated apk there, it would download it and install.
I wrote this class (copying from another Stackoverflow question):
class updateApp extends AsyncTask<String, Void, String> {
protected String doInBackground(String... sUrl) {
File file = new File(Environment.getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS),"updates");
if(!file.mkdir()){
}
File f = new File(file.getAbsolutePath(),"YourApp.apk");
Uri fUri = FileProvider.getUriForFile(MainActivity.this,"com.aktuna.vtv.monitor.fileprovider",f);
String path = fUri.getPath();
try {
URL url = new URL(sUrl[0]);
URLConnection connection = url.openConnection();
connection.connect();
InputStream input = new BufferedInputStream(url.openStream());
OutputStream output = new FileOutputStream(path);
byte data[] = new byte[1024];
int count;
while ((count = input.read(data)) != -1) {
output.write(data, 0, count);
}
output.flush();
output.close();
input.close();
} catch (Exception e) {
Log.e("YourApp", "Well that didn't work out so well...");
Log.e("YourApp", e.getMessage());
}
return path;
}
#Override
protected void onPostExecute(String path) {
Intent i = new Intent();
i.setAction(Intent.ACTION_VIEW);
i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
File file = new File(Environment.getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS),"updates");
File f = new File(file.getAbsolutePath(),"YourApp.apk");
Uri fUri = FileProvider.getUriForFile(MainActivity.this,"com.aktuna.vtv.monitor.fileprovider",f);
i.setDataAndType(fUri, "application/vnd.android.package-archive" );
myCtx.startActivity(i);
}
}
It seems to download the file successfully. And then it sends the file in the intent to the installer (I can see this because the packageinstaller selection prompt comes)
But then it does not install the new apk.
Since the previous Stackoverflow question is 7 years old, I thought that updating with no user interaction may be forbidden in new API levels.
But I am not sure.
How can I troubleshoot this further ?
Also, I am open to any suggestions to achieve this, maybe something making use of older API levels or anything that would solve the "updating with no internet access through an internal static URL" issue.
Thanks.
I followed recommendation from #keag and it worked.
1.
With no "root" on the device, I made the app "device-owner"
For this I added a device admin receiver class. SampleAdminReceiver.class:
import android.app.admin.DeviceAdminReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class SampleAdminReceiver extends DeviceAdminReceiver {
void showToast(Context context, CharSequence msg) {
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
}
#Override
public void onEnabled(Context context, Intent intent) {
showToast(context, "Device admin enabled");
}
#Override
public void onDisabled(Context context, Intent intent) {
showToast(context, "Device admin disabled");
}
}
added receiver to the manifest:
<receiver
android:name=".SampleAdminReceiver"
android:description="#string/app_name"
android:label="#string/app_name"
android:permission="android.permission.BIND_DEVICE_ADMIN" >
<meta-data
android:name="android.app.device_admin"
android:resource="#xml/device_admin_receiver" />
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver>
Then using the adb interface I run the following dpm command:
$ dpm set-device-owner com.sample.app/.SampleAdminReceiver
Added following permission to manifest :
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
The with the following function I am able to install the apk from URL:
public static boolean installPackageX(final Context context, final String url)
throws IOException {
//Use an async task to run the install package method
AsyncTask<Void,Void,Void> task = new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... voids) {
try {
PackageInstaller packageInstaller = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
packageInstaller = context.getPackageManager().getPackageInstaller();
}
PackageInstaller.SessionParams params = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
}
// set params
int sessionId = 0;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
sessionId = packageInstaller.createSession(params);
}
PackageInstaller.Session session = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
session = packageInstaller.openSession(sessionId);
}
OutputStream out = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
out = session.openWrite("COSU", 0, -1);
}
//get the input stream from the url
HttpURLConnection apkConn = (HttpURLConnection) new URL(url).openConnection();
InputStream in = apkConn.getInputStream();
byte[] buffer = new byte[65536];
int c;
while ((c = in.read(buffer)) != -1) {
out.write(buffer, 0, c);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
session.fsync(out);
}
in.close();
out.close();
//you can replace this intent with whatever intent you want to be run when the applicaiton is finished installing
//I assume you have an activity called InstallComplete
Intent intent = new Intent(context, MainActivity.class);
intent.putExtra("info", "somedata"); // for extra data if needed..
Random generator = new Random();
PendingIntent i = PendingIntent.getActivity(context, generator.nextInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
session.commit(i.getIntentSender());
}
} catch (Exception ex){
ex.printStackTrace();
Log.e("AppStore","Error when installing application. Error is " + ex.getMessage());
}
return null;
}
};
task.execute(null,null);
return true;
}
After that, it is just a matter of automating the process.
Btw, following code in the app is useful for removing "device owner" property.
DevicePolicyManager dpm = (DevicePolicyManager) getApplicationContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
dpm.clearDeviceOwnerApp(getApplicationContext().getPackageName());
I'm writing Espresso tests to run on my React Native app. I'm using Espresso-Intents to simulate the user taking a picture, with the following code:
public void takePicture() {
Instrumentation.ActivityResult result = createImageCaptureActivityResultStub();
intending(hasAction(MediaStore.ACTION_IMAGE_CAPTURE)).respondWith(result);
clickText("Camera");
}
public static Bitmap getBitmapFromURL(String src) {
try {
URL url = new URL(src);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream input = connection.getInputStream();
Bitmap myBitmap = BitmapFactory.decodeStream(input);
return myBitmap;
} catch (IOException e) {
// Log exception
System.out.println(e.toString());
return null;
}
}
private Instrumentation.ActivityResult createImageCaptureActivityResultStub() {
// Put the drawable in a bundle.
Bundle bundle = new Bundle();
bundle.putParcelable("data", getBitmapFromURL("https://vignette.wikia.nocookie.net/the-feed-your-pets-community/images/6/69/Banana.png/revision/latest?cb=20180527162222"));
// Create the Intent that will include the bundle.
Intent resultData = new Intent();
resultData.putExtras(bundle);
// Create the ActivityResult with the Intent.
return new Instrumentation.ActivityResult(Activity.RESULT_OK, resultData);
}
When running, the camera intent is successfully stubbed, but the view which should display the photo is stuck in its loading state.
In debugging, I have discovered that getBitmapFromURL is not the problem, because bundle contains the correct image.
Could anyone suggest where the problem may lie, or how best to debug further?
I'm using an AsyncTask to download a file. But while it downloads the file, the UI gets freeze, and I have to wait the file to finish downloading to be able to do something. I've looked other threads, but I only found problems like running the task using .get().
Here's how I'm calling the AsyncTask
new DownloadFileFromUrl().execute("url");
and here's the AsyncTask
class DownloadFileFromURL extends AsyncTask<String, Integer, LineData> {
String fileDir = getApplicationContext().getFilesDir() + "/data.txt";
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected LineData doInBackground(String... f_url) {
int count;
//Download file--------------------------
try {
URL url = new URL(f_url[0]);
URLConnection conection = url.openConnection();
conection.connect();
int lenghtOfFile = conection.getContentLength();
InputStream input = new BufferedInputStream(url.openStream(),
8192);
OutputStream output = new FileOutputStream(fileDir);
byte data[] = new byte[1024];
long total = 0;
while ((count = input.read(data)) != -1) {
total += count;
publishProgress((int) total * 100 / lenghtOfFile);
output.write(data, 0, count);
}
output.flush();
output.close();
input.close();
} catch (Exception e) {
Log.e("Error: ", e.getMessage());
}
return null;
}
#Override
protected void onPostExecute(LineData data) {
setDataFor24Hours();
setDataFor48Hours();
setDataFor72Hours();
showDataFor24Hours();
setList();
Log.e("Downloader: ", "done!");
}
}
I've made it to send Done! to the monitor when it finishes, and at this moment the UI gets unfreeze also, so the problem is in the AsyncTask.
I hope anyone can help me. Thanks in advance!
AsyncTask design in such a way that we can perform an action without freezing UI.
But doInBackgroundis only run in the background. But onPostExecute run on UI thread. So maybe your problem is in onPostExecute
Try to define like this.
new DownloadFileFromUrl().execute("url");
I've been looking all over the place for this, and the only answer I've had was "use Pair", but I can't get this to work either.
Here's what I need to:
In Asynctask I need to update both a progress bar, and text. Because of this my Asynctask generic cannot be just Integer and not just String, but both. This is so I can have both classes within the "onProgressUpdate" method.
Can somebody give me some example or links as to how I add the strings and increase the integer in "doInBackground", and how to implement this in the "onProgressUpdate"?
Thank you very much!
Can you create your own simple class to hold the variables and then pass that?
Or, what if you pass a string that you can parse and get the values you need? If you take your first string += ":" + int, then make use of something like
String myString = passedString.substring(0, passedString.lastIndexOf(":")))
int i = Integer.parseInt(passedString.substring(passedString.lastIndexOf(":")+1));
As far as I understand your question; there are mainly two things which you want to do:
1) Handle a UI thread while in the doIneBackground().
2) Implement the onProgressUpdate().
Basically we shouldn't try to access the UI thread while a background process is running.
The reason for that is very clear... # OS level there will be so many thread will be running.And in that case It will be chaos on the screen, if we can update UI from background thread.
For the 2nd one I would like recommend you to take a look at this example:
ProgressDialog mProgressDialog;
mProgressDialog = new ProgressDialog(YourActivity.this);
mProgressDialog.setMessage("A message");
mProgressDialog.setIndeterminate(true);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setCancelable(true);
final DownloadTask downloadTask = new DownloadTask(YourActivity.this);
downloadTask.execute("the url to the file you want to download");
mProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
downloadTask.cancel(true);
}
});
In the AsynTask:
private class DownloadTask extends AsyncTask<String, Integer, String> {
private Context context;
public DownloadTask(Context context) {
this.context = context;
}
#Override
protected String doInBackground(String... sUrl) {
// take CPU lock to prevent CPU from going off if the user
// presses the power button during download
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
getClass().getName());
wl.acquire();
try {
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
try {
URL url = new URL(sUrl[0]);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
// expect HTTP 200 OK, so we don't mistakenly save error report
// instead of the file
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK)
return "Server returned HTTP " + connection.getResponseCode()
+ " " + connection.getResponseMessage();
// this will be useful to display download percentage
// might be -1: server did not report the length
int fileLength = connection.getContentLength();
// download the file
input = connection.getInputStream();
output = new FileOutputStream("/sdcard/file_name.extension");
byte data[] = new byte[4096];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
// allow canceling with back button
if (isCancelled())
return null;
total += count;
// publishing the progress....
if (fileLength > 0) // only if total length is known
publishProgress((int) (total * 100 / fileLength));
output.write(data, 0, count);
}
} catch (Exception e) {
return e.toString();
} finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
}
catch (IOException ignored) { }
if (connection != null)
connection.disconnect();
}
} finally {
wl.release();
}
return null;
}}
The method above (doInBackground) runs always on a background thread. You shouldn't do any UI tasks there. On the other hand, the onProgressUpdate and onPreExecute run on the UI thread, so there you can change the progress bar:
#Override
protected void onPreExecute() {
super.onPreExecute();
mProgressDialog.show();
}
#Override
protected void onProgressUpdate(Integer... progress) {
super.onProgressUpdate(progress);
// if we get here, length is known, now set indeterminate to false
mProgressDialog.setIndeterminate(false);
mProgressDialog.setMax(100);
mProgressDialog.setProgress(progress[0]);
}
#Override
protected void onPostExecute(String result) {
mProgressDialog.dismiss();
if (result != null)
Toast.makeText(context,"Download error: "+result, Toast.LENGTH_LONG).show();
else
Toast.makeText(context,"File downloaded", Toast.LENGTH_SHORT).show();
}
Regards
Sathya