Skip to content

Commit

Permalink
Merge branch 'release/2.0.4'
Browse files Browse the repository at this point in the history
  • Loading branch information
Inigo Lopez de Heredia committed Oct 16, 2014
2 parents 715a60c + ecbe66b commit 5bae935
Show file tree
Hide file tree
Showing 14 changed files with 270 additions and 158 deletions.
3 changes: 2 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.akvo.flow"
android:versionCode="1"
android:versionName="2.0.3.3" >
android:versionName="2.0.4" >

<uses-sdk
android:minSdkVersion="8"
Expand Down Expand Up @@ -76,6 +76,7 @@
android:name=".activity.AppUpdateActivity"
android:label="@string/app_update_activity"
android:theme="@style/Flow.Dialog"
android:launchMode="singleTop"
android:configChanges="locale|layoutDirection" >
</activity>

Expand Down
129 changes: 79 additions & 50 deletions app/src/main/java/org/akvo/flow/activity/AppUpdateActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import android.view.Window;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import org.akvo.flow.R;
Expand All @@ -42,22 +43,21 @@
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
import java.util.Map;

public class AppUpdateActivity extends Activity implements View.OnClickListener {
public class AppUpdateActivity extends Activity {
public static final String EXTRA_URL = "url";
public static final String EXTRA_VERSION = "version";
public static final String EXTRA_CHECKSUM = "md5Checksum";

private static final String TAG = AppUpdateActivity.class.getSimpleName();
private static final int IO_BUFFER_SIZE = 8192;
private static final int MAX_PROGRESS = 100;

private Button mButton;
private Button mInstallBtn;
private ProgressBar mProgress;
private UpdateAsyncTask mTask;

String mUrl, mVersion;
String mUrl, mVersion, mMd5Checksum;

@Override
protected void onCreate(Bundle savedInstanceState) {
Expand All @@ -67,34 +67,79 @@ protected void onCreate(Bundle savedInstanceState) {

mUrl = getIntent().getStringExtra(EXTRA_URL);
mVersion = getIntent().getStringExtra(EXTRA_VERSION);
mMd5Checksum = getIntent().getStringExtra(EXTRA_CHECKSUM);

mInstallBtn = (Button)findViewById(R.id.install_btn);
mProgress = (ProgressBar)findViewById(R.id.progress);
mButton = (Button)findViewById(R.id.cancel_btn);

mProgress.setMax(MAX_PROGRESS);// Values will be in percentage
mButton.setOnClickListener(this);
}

@Override
public void onClick(View v) {
if (!isRunning()) {
mButton.setText(R.string.cancelbutton);
mTask = new UpdateAsyncTask();
mTask.execute();
// If the file is already downloaded, just prompt the install text
final String filename = checkLocalFile();
if (filename != null) {
TextView updateTV = (TextView)findViewById(R.id.update_text);
updateTV.setText(R.string.clicktoinstall);
mInstallBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
PlatformUtil.installAppUpdate(AppUpdateActivity.this, filename);
}
});
} else {
// Stop the update process
mButton.setText(R.string.download_and_install);
mTask.cancel(true);
finish();
mInstallBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mInstallBtn.setEnabled(false);
mTask = new UpdateAsyncTask();
mTask.execute();
}
});
}

Button cancelBtn = (Button)findViewById(R.id.cancel_btn);
cancelBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
cancel();
}
});
}

/**
* Check out previously downloaded files. If the APK update is already downloaded,
* and the MD5 checksum matches, the file is considered downloaded.
*
* @return filename of the already downloaded file, if exists. Null otherwise
*/
private String checkLocalFile() {
final String latestVersion = FileUtil.checkDownloadedVersions(this);
if (latestVersion != null) {
if (mMd5Checksum != null) {
// The file was found, but we need to ensure the checksum matches,
// to ensure the download succeeded
File file = new File(latestVersion);
if (!mMd5Checksum.equals(FileUtil.hexMd5(file))) {
file.delete();// Wipe corrupted files
return null;
}
}
return latestVersion;
}
return null;
}

private void cancel() {
if (isRunning()) {
mTask.cancel(true);// Stop the update process
}
finish();
}

@Override
public void onBackPressed() {
public void onDestroy() {
if (isRunning()) {
mTask.cancel(true);
}
super.onBackPressed();
super.onDestroy();
}

private boolean isRunning() {
Expand Down Expand Up @@ -137,7 +182,8 @@ protected void onProgressUpdate(Integer... progress) {
protected void onPostExecute(String filename) {
if (TextUtils.isEmpty(filename)) {
Toast.makeText(AppUpdateActivity.this, R.string.apk_upgrade_error, Toast.LENGTH_SHORT).show();
mButton.setText(R.string.retry);
mInstallBtn.setText(R.string.retry);
mInstallBtn.setEnabled(true);
return;
}

Expand Down Expand Up @@ -222,17 +268,18 @@ private boolean downloadApk(String location, String localPath) {
final int status = conn.getResponseCode();

if (status == HttpStatus.SC_OK) {
Map<String, List<String>> headers = conn.getHeaderFields();
String etag = headers != null ? getHeader(headers, "ETag") : null;
etag = etag != null ? etag.replaceAll("\"", "") : null;// Remove quotes
final String checksum = FileUtil.hexMd5(new File(localPath));
if (TextUtils.isEmpty(checksum)) {
throw new IOException("Downloaded file is not available");
}

if (etag != null && etag.equals(checksum)) {
ok = true;
} else {
Log.e(TAG, "ETag comparison failed. Remote: " + etag + " Local: " + checksum);
ok = false;
if (mMd5Checksum == null) {
// If we don't have a checksum yet, try to get it form the ETag header
String etag = conn.getHeaderField("ETag");
mMd5Checksum = etag != null ? etag.replaceAll("\"", "") : null;// Remove quotes
}
// Compare the MD5, if found. Otherwise, rely on the 200 status code
ok = mMd5Checksum == null || mMd5Checksum.equals(checksum);
} else {
Log.e(TAG, "Wrong status code: " + status);
ok = false;
Expand All @@ -243,29 +290,11 @@ private boolean downloadApk(String location, String localPath) {
if (conn != null) {
conn.disconnect();
}
try {
out.close();
} catch (Exception ignored) {}
try {
in.close();
} catch (Exception ignored) {}
FileUtil.close(in);
FileUtil.close(out);
}

return ok;
}

/**
* Helper function to get a particular header field from the header map
* @param headers
* @param key
* @return header value, if found, false otherwise.
*/
private String getHeader(Map<String, List<String>> headers, String key) {
List<String> values = headers.get(key);
if (values != null && values.size() > 0) {
return values.get(0);
}
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,7 @@ public void onClick(DialogInterface dialog, int id) {
response.setRespondentId(mSurveyInstanceId);
mDatabase.createOrUpdateSurveyResponse(response);
} else {
event.getSource().setResponse(null, true);// Invalidate previous response
mQuestionResponses.remove(questionId);
mDatabase.deleteResponse(mSurveyInstanceId, questionId);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,31 +136,7 @@ public void onClick(DialogInterface dialog, int which) {
startService(new Intent(this, BootstrapService.class));
startService(new Intent(this, ExceptionReportingService.class));
startService(new Intent(this, DataSyncService.class));
checkApkUpdates();
}
}

/**
* Check if new FLOW versions are available to installAppUpdate.
* First we check the local storage, to see if the version is already
* downloaded. If so, we display a dialog to request the user to installAppUpdate it.
* Otherwise, we trigger the ApkUpdateService to check for updates.
*/
private void checkApkUpdates() {
final String latestVersion = FileUtil.checkDownloadedVersions(this);
if (latestVersion != null) {
// In your face!
ViewUtil.showConfirmDialog(R.string.updatedownloaded, R.string.clicktoinstall, this, true,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
PlatformUtil.installAppUpdate(SurveyGroupListActivity.this, latestVersion);
}
}
);
} else {
Intent apkUpdateIntent = new Intent(this, ApkUpdateService.class);
startService(apkUpdateIntent);
startService(new Intent(this, ApkUpdateService.class));
}
}

Expand Down
23 changes: 20 additions & 3 deletions app/src/main/java/org/akvo/flow/activity/UserEditActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import android.app.Activity;
import android.database.Cursor;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.view.Window;
import android.widget.Button;
Expand Down Expand Up @@ -64,9 +65,11 @@ protected void onCreate(Bundle savedInstanceState) {

saveButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
saveState();
setResult(RESULT_OK);
finish();
if (validate()) {
saveState();
setResult(RESULT_OK);
finish();
}
}
});
}
Expand Down Expand Up @@ -121,4 +124,18 @@ private void saveState() {
databaseAdaptor.createOrUpdateUser(userId, name, email);
}

/**
* Check values and set up any error state accordingly
* @return true if no error is found, false otherwise
*/
private boolean validate() {
// Validate username. Password can be empty
String name = StringUtil.ControlCommaToSPace(displayName.getText().toString()).trim();
if (TextUtils.isEmpty(name)) {
displayName.setError(getString(R.string.error_value_required));
return false;
}
return true;
}

}
12 changes: 4 additions & 8 deletions app/src/main/java/org/akvo/flow/domain/QuestionResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@

package org.akvo.flow.domain;

import java.util.StringTokenizer;

import org.akvo.flow.util.ConstantUtil;

public class QuestionResponse {
Expand Down Expand Up @@ -83,15 +81,13 @@ public boolean isValid() {
if (value == null || value.trim().length() == 0) {
return false;
}
// now check that, if it's a geo question, we have something
// specified
// now check that, if it's a geo question, we have something specified
if (ConstantUtil.GEO_RESPONSE_TYPE.equals(type)) {
// at this point, we know value isn't null
StringTokenizer strTok = new StringTokenizer(value, "|");
if (strTok.countTokens() >= 2) {
String[] tokens = value.split("\\|", -1);
if (tokens.length >= 2) {
// at least the first 2 tokens must be numeric
for (int i = 0; i < 2; i++) {
String token = strTok.nextToken();
String token = tokens[i];
try {
if (token.trim().length() > 0) {
Double.parseDouble(token);
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/java/org/akvo/flow/service/ApkUpdateService.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,14 @@ private void checkUpdates() {
if (PlatformUtil.isNewerVersion(PlatformUtil.getVersionName(this), ver)
&& !TextUtils.isEmpty(location)) {
// There is a newer version. Fire the 'Download and Install' Activity.
String md5Checksum = json.optString("md5Checksum", null);
Intent i = new Intent(this, AppUpdateActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.putExtra(AppUpdateActivity.EXTRA_URL, location);
i.putExtra(AppUpdateActivity.EXTRA_VERSION, ver);
if (!TextUtils.isEmpty(md5Checksum) && !md5Checksum.equalsIgnoreCase("null")) {
i.putExtra(AppUpdateActivity.EXTRA_CHECKSUM, md5Checksum);
}
startActivity(i);
}
}
Expand Down
Loading

0 comments on commit 5bae935

Please sign in to comment.