I referenced a question here about how one might approach (outside of Google Play) having an app essentially update itself. For testing, I simply wanted to try to see if I could get it to download and install. Unfortunately, I get a parse error.
I would greatly appreciate any help:
A snippet from the class that calls the AsyncTask class:
public class downloadReceiver extends BroadcastReceiver {
private Context context;
private long localUpdate;
private long remoteUpdate = 20;
#Override
public void onReceive(final Context c, Intent i) {
new Thread(new Runnable() {
#Override
public void run() {
SharedPreferences preferences = c.getSharedPreferences("config", c.MODE_PRIVATE);
final String store = preferences.getString("store", "");
final String id = preferences.getString("id", "");
final long lastUpdated = preferences.getLong("updated", 0);
// autoUpdate app
appUpdater updater = new appUpdater(c);
try {
updater.execute(new URL("http://midamcorp.com/myApp.php"));
} catch (Exception e) {Log.e(this.getClass().toString(), " " + e.getMessage()); }
and the appUpdater class:
public class appUpdater extends AsyncTask<URL, String, String> {
private Context c;
public appUpdater(Context context) {
this.c = context;
}
protected String doInBackground(URL... appUrl) {
String location = c.getFilesDir() + "/app.apk";
try {
URL url = appUrl[0];
URLConnection con = url.openConnection();
con.connect();
InputStream input = new BufferedInputStream(url.openStream());
OutputStream output = new FileOutputStream(location);
byte[] buffer = new byte[1024];
int read = 0;
while ((read = input.read(buffer)) != -1) {
output.write(buffer, 0, read);
}
output.close();
input.close();
} catch(Exception e){
Log.e(this.getClass().toString(), " " + e.getMessage());
}
return location;
}
#Override
protected void onPostExecute(String saveLocation) {
Intent i = new Intent();
i.setAction(Intent.ACTION_VIEW);
Log.i("Location of app is: ", " " + saveLocation);
i.setDataAndType(Uri.fromFile(new File(saveLocation)), "application/vnd.android.package-archive");
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
c.startActivity(i);
}
}
Please note, the URL is linked to a PHP file that forces a download because the server I have it on has trouble with .apk files.
Your primary problem is that the installer does not have access to your portion of internal storage (getFilesDir()). Use external storage.
I also recommend that you call flush(), getFD().sync(), and close() in succession on your FileOutputStream, before trying to install the app.
Related
I am developing an app and I use Socket.io on it, I initialize the socket in a class that extends Application and looks like this:
public class Inicio extends Application{
private Socket mSocket;
private SharedPreferences spref;
#Override
public void onCreate(){
super.onCreate();
try{
spref = getSharedPreferences("accountData", Context.MODE_PRIVATE);
IO.Options op = new IO.Options();
op.forceNew = true;
op.reconnection = true;
op.query = "tok=" + spref.getString("sessiontoken", "") + "&usr=" + spref.getString("userid", "");
mSocket = IO.socket(Constants.serverAddress, op);
}catch(URISyntaxException e){
throw new RuntimeException(e);
}
}
public Socket getmSocket(){
return mSocket;
}
}
So I can get and use the same socket instance in other parts of my application's code calling the following way:
Inicio appClass = (Inicio) getApplication();
mSocket = appClas.getmSocket();
mSocket.connect();
But there is a small problem that motivated me to post this question, can you see when I call to SharedPreferences in the Application class? I do this because I need to send the session token and user account ID to properly start the socket connection with my server, the problem is:
Imagine that a user opens the app for the first time and does not have an account yet, he will login or register and then the session token and user ID will be saved to SharedPreferences, but when the app started and ran the Application class, SharedPreferences was still empty and did not have the required token and user ID to establish the connection, so the user would have to reopen the app now to be able to use the socket successfully.
So I ask you: What are my alternative options for solving the problem? Is there another structure besides the Application class that I could use to not suffer from this problem? Or is there some way to bypass this problem?
What I'm doing to get around the problem for now is to restart the app programmatically when login occurs but I believe this is looks like a sad joke and not the ideal way to do it.
Thanks, I apologize for this long question of mine, but I'll be grateful for any help.
Separate your soket creation logic like below:
private void createSoket() {
spref = getSharedPreferences("accountData", Context.MODE_PRIVATE);
String sessiontoken = spref.getString("sessiontoken", "");
String userId = spref.getString("userid", "");
if(!(TextUtils.isEmpty(sessiontoken) || TextUtils.isEmpty(userId))) {
try {
IO.Options op = new IO.Options();
op.forceNew = true;
op.reconnection = true;
op.query = "tok=" + sessiontoken + "&usr=" + userId;
mSocket = IO.socket(Constants.serverAddress, op);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
}
And when required soket check null and create before pass the instance.
public Socket getmSocket(){
if(mSoket == null)
createSoket();
return mSocket;
}
N.B: Without valid settionToken and userId, soket is null
This is complete Application class:
public class Inicio extends Application{
private Socket mSocket;
private SharedPreferences spref;
#Override
public void onCreate(){
super.onCreate();
createSoket();
}
private void createSoket() {
spref = getSharedPreferences("accountData", Context.MODE_PRIVATE);
String sessiontoken = spref.getString("sessiontoken", "");
String userId = spref.getString("userid", "");
if(!(TextUtils.isEmpty(sessiontoken) || TextUtils.isEmpty(userId))) {
try {
IO.Options op = new IO.Options();
op.forceNew = true;
op.reconnection = true;
op.query = "tok=" + sessiontoken + "&usr=" + userId;
mSocket = IO.socket(Constants.serverAddress, op);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
}
public Socket getmSocket(){
if(mSoket == null)
createSoket();
return mSocket;
}
}
I'm trying to add a class in my application by which I can download different type of files instead of pdf on API 23 and lower.
I have tested my codes on API 24 and upper and I can easily download pdf files but I don't know why it's not working on API <= 23.
public class FileDownloader {
private static final int MEGA_BYTE = 1024 * 1024;
public interface OnDownloadListener{
void onStarted();
void onProgressUpdate(int upd);
void onFinished(String result);
void onError(Exception e);
}
// usually, subclasses of AsyncTask are declared inside the activity class.
// that way, you can easily modify the UI thread from here
public static class DownloadTask extends AsyncTask<String, Integer, String> {
private Context context;
private OnDownloadListener onDownloadListener;
public DownloadTask(Context context, OnDownloadListener onDownloadListener) {
this.context = context;
this.onDownloadListener = onDownloadListener;
}
#Override
protected String doInBackground(String... str) {
// 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(str[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(str[1]); // /sdcard/file_name.extension
byte data[] = new byte[MEGA_BYTE];
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) {
e.printStackTrace();
if(onDownloadListener != null){
onDownloadListener.onError(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;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
if(onDownloadListener != null){
onDownloadListener.onStarted();
}
}
#Override
protected void onProgressUpdate(Integer... progress) {
super.onProgressUpdate(progress);
// if we get here, length is known, now set indeterminate to fals
if(onDownloadListener != null){
onDownloadListener.onProgressUpdate(progress[0]);
}
}
#Override
protected void onPostExecute(String result) {
if(onDownloadListener != null){
onDownloadListener.onFinished(result);
}
}
}
}
When "HttpURLConnection" tries to connect it returns error code 404 which means "HTTP 404 Not Found" but on API >= 24 it works fine and also I can download these files via web browsers too.
I also tried to use "DownloadManager" class but it returns "Failed" when I start downloading pdf files on API <= 23.
How can I fix this problem on API <= 23?!!
Thanks in advance.
Set your minSdkVersion to maybe 15. This will make your app compatible with devices API 15 and above
I'm trying to save a single int to a file in my Android project although i cant get my write function to work.
My JSON file:
{
"user":
{
"userid":"0"
}
}
My code:
public String getJsonFile() {
String jsonLocation = "";
try {
InputStream is = getAssets().open("useriidd.json");
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
jsonLocation = new String(buffer, "UTF-8");
} catch (IOException ex) {
ex.printStackTrace();
}
return jsonLocation;
}
public void jsonRead(){
try {
JSONObject reader = new JSONObject(getJsonFile());
JSONObject sys = reader.getJSONObject("user");
userid = Integer.parseInt(sys.getString("userid"));
}
catch (final JSONException e) {
Log.e("asdasd", e.getMessage());
}
}
public void jsonWrite(){
try {
JSONObject writer = new JSONObject(getJsonFile());
JSONObject sys = writer.getJSONObject("user");
sys.put("userid", Integer.toString(userid));
Log.d("asdasd", getJsonFile());
}
catch (final JSONException e) {
Log.e("asdasd", e.getMessage());
}
}
I need the userid value to also be saved if i exit the app and relaunch it.
You should store it in Preferences, SharedPreferences or in the file on external storage (SD card). You cannot (or should not) edit the assets in run-time.
Declare on Class level
public static final String KEY = "key";
private String value = "you can place any value";
private SharedPreferences sharedPreferences;
onCreate Method
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(YourMainActivity.this);
sharedPreferences.edit().putString(KEY, value);
onResume Method
value = sharedPreferences.getString(KEY, "defaultValue");
You can also do this step on onPause Method
sharedPreferences.edit().putString(KEY, value);
I have been trying for several days to connect Amazon S3 to my Android project. I downloaded the example "https://github.com/awslabs/aws-sdk-android-samples" "S3TransferUtilitySample" and everything works fine on it, I see the files through the aws admin panel.
I copied into my project "Constants.java" with the working settings, also copied "Util.java" without changes.
The purpose of my project is to record the file from the microphone and transfer it to the cloud.
Here is the singleton that should implement this operations :
public class RecorderHelper {
private static final String TAG = "UploadActivity";
private static TransferUtility sTransferUtility;
static private Util util;
static RecorderHelper singleton;
static Boolean RecordStateRecording;
private static MediaRecorder recorder;
private final String RECORD = Environment.getExternalStorageDirectory() + "/record.aac";
String fileName;
private RecorderHelper() {
}
public static RecorderHelper getSingleton(Context context) {
if (singleton == null) {
RecordStateRecording = false;
singleton = new RecorderHelper();
util = new Util();
AmazonS3Client s3Client = util.getS3Client(context);
sTransferUtility = util.getTransferUtility(context);
}
;
return singleton;
}
public void StopRecording() {
try {
if (RecordStateRecording) {
recorder.stop();
recorder.reset();
recorder.release();
AACTrackImpl aacTrack = new AACTrackImpl(new FileDataSourceImpl(RECORD));
if (aacTrack.getSamples().size() > 1000) {
CroppedTrack aacTrackShort = new CroppedTrack(aacTrack, aacTrack.getSamples().size() - 1000, aacTrack.getSamples().size());
Movie movie = new Movie();
movie.addTrack(aacTrackShort);
Container mp4file = new DefaultMp4Builder().build(movie);
FileChannel fc = new FileOutputStream(new File(fileName)).getChannel();
mp4file.writeContainer(fc);
fc.close();
aacTrackShort.close();
aacTrack.close();
} else {
aacTrack.close();
}
}
File file = new File(RECORD);
TransferObserver observer = sTransferUtility.upload(Constants.BUCKET_NAME, file.getName(),
file);
observer.setTransferListener(new UploadListener());
} catch (Exception e) {
Log.e("RECORD", e.getMessage());
}
RecordStateRecording = false;
}
public void StartNewRecording(String UUID) {
StopRecording();
recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.AAC_ADTS);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
fileName = Environment.getExternalStorageDirectory() + "/" + UUID + ".aac";
recorder.setOutputFile(RECORD);
try {
recorder.prepare();
} catch (IOException e) {
e.printStackTrace();
}
recorder.start(); // Recording is now started
RecordStateRecording = true;
}
private class UploadListener implements TransferListener {
// Simply updates the UI list when notified.
#Override
public void onError(int id, Exception e) {
Log.e(TAG, "Error during upload: " + id, e);
}
#Override
public void onProgressChanged(int id, long bytesCurrent, long bytesTotal) {
Log.d(TAG, String.format("onProgressChanged: %d, total: %d, current: %d",
id, bytesTotal, bytesCurrent));
}
#Override
public void onStateChanged(int id, TransferState newState) {
Log.d(TAG, "onStateChanged: " + id + ", " + newState);
}
}
}
However, the file does not appear in the cloud and the listener tells me about the 405 error. Here is the full text.
Can anyone tell me what I'm doing wrong?
Unable to unmarshall error response (Attempt to invoke virtual method
'boolean java.lang.String.equals(java.lang.Object)' on a null object
reference). Response Code: 405, Response Text:
I'm ussing the latest SDK :
compile 'com.amazonaws:aws-android-sdk-s3:2.6.+'
Not sure about stacktrace because because i just get a callback to my listener about the transfer fails.
API 22
I'm currently developing an app and have the following issue.
While using NFC for device owner provisioning, I would like to send a string, which would be used by the new device owner app.
I'm aware of the standard MIME properties for device owner provisioning, found here
Here's a snippet that can give you a better visual of my issue. Notice the "myCustomValue" property.
Properties properties = new Properties();
properties.put("myCustomValue", value);
properties.put(DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME, "com.example.some.app");
try {
properties.store(stream, "NFC Provisioning");
ndefMessage = new NdefMessage(new NdefRecord[{NdefRecord.createMime(DevicePolicyManager.MIME_TYPE_PROVISIONING_NFC, stream.toByteArray())});
} catch (IOException e) {
}
This snippet lies inside
public NdefMessage createNdefMessage(NfcEvent event)
and you can find a template here
In case this is possible, I also would like to know how to retrieve that string value as soon as the provisioned app has started.
The code below should be what you're looking for. For brevity, I only set the package name plus two strings that will be sent to your DeviceAdminReceiver.
#Override
public NdefMessage createNdefMessage(NfcEvent event) {
try {
Properties p = new Properties();
p.setProperty(
DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME,
"com.example.some.app");
Properties extras = new Properties();
extras.setProperty("Key1", "TestString1");
extras.setProperty("Key2", "TestString2");
StringWriter sw = new StringWriter();
try{
extras.store(sw, "admin extras bundle");
p.put(DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE,
sw.toString());
Log.d(TAG, "Admin extras bundle=" + p.get(
DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE));
} catch (IOException e) {
Log.e(TAG, "Unable to build admin extras bundle");
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
OutputStream out = new ObjectOutputStream(bos);
p.store(out, "");
final byte[] bytes = bos.toByteArray();
NdefMessage msg = new NdefMessage(NdefRecord.createMime(
DevicePolicyManager.MIME_TYPE_PROVISIONING_NFC, bytes));
return msg;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
This next snippet will go in your DeviceAdminReceiver in order to receive the "Admin Extras"... If you don't override onReceive, onProfileProvisioningComplete will need to be overridden with EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE handled in there instead.
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive " + intent.getAction());
if (ACTION_PROFILE_PROVISIONING_COMPLETE.equals(intent.getAction())) {
PersistableBundle extras = intent.getParcelableExtra(
EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE);
Log.d(TAG, "onReceive Extras:" + extras.getString("Key1") + " / " + extras.getString("Key2"));
}
}
onProfileProvisioningComplete will be overwritten like this.
#Override
public void onProfileProvisioningComplete(final Context context, final Intent intent)
{
final PersistableBundle extras = intent.getParcelableExtra(DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE);
Log.d(TAG, "onProfileProvisioningComplete Extras:" + extras.getString("Key1") + " / " + extras.getString("Key2"));
}