From f8daccffc061e2f05f6605913c19d4aa807eaddb Mon Sep 17 00:00:00 2001 From: cyBerta Date: Mon, 9 Nov 2020 15:37:31 +0100 Subject: initial auto-update implementation: introducing fatweb flavor, pgpverify go library and bitmask core library, basic update mechanism --- .../appUpdate/DownloadBroadcastReceiver.java | 114 ++++++++++ .../appUpdate/DownloadConnector.java | 116 +++++++++++ .../appUpdate/DownloadNotificationManager.java | 113 ++++++++++ .../appUpdate/DownloadService.java | 82 ++++++++ .../appUpdate/DownloadServiceCommand.java | 81 ++++++++ .../appUpdate/FileProviderUtil.java | 52 +++++ .../appUpdate/UpdateDownloadManager.java | 231 +++++++++++++++++++++ 7 files changed, 789 insertions(+) create mode 100644 app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadBroadcastReceiver.java create mode 100644 app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadConnector.java create mode 100644 app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadNotificationManager.java create mode 100644 app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadService.java create mode 100644 app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadServiceCommand.java create mode 100644 app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/FileProviderUtil.java create mode 100644 app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/UpdateDownloadManager.java (limited to 'app/src/fatweb/java/se.leap.bitmaskclient') diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadBroadcastReceiver.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadBroadcastReceiver.java new file mode 100644 index 00000000..6613d394 --- /dev/null +++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadBroadcastReceiver.java @@ -0,0 +1,114 @@ +/** + * Copyright (c) 2020 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package se.leap.bitmaskclient.appUpdate; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.util.Log; +import android.widget.Toast; + +import java.io.File; + +import se.leap.bitmaskclient.Constants; + +import static android.app.Activity.RESULT_CANCELED; +import static se.leap.bitmaskclient.Constants.BROADCAST_DOWNLOAD_SERVICE_EVENT; +import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_CODE; +import static se.leap.bitmaskclient.appUpdate.DownloadConnector.APP_TYPE; +import static se.leap.bitmaskclient.appUpdate.DownloadService.DOWNLOAD_FAILED; +import static se.leap.bitmaskclient.appUpdate.DownloadService.DOWNLOAD_PROGRESS; +import static se.leap.bitmaskclient.appUpdate.DownloadService.NO_NEW_VERISON; +import static se.leap.bitmaskclient.appUpdate.DownloadService.PROGRESS_VALUE; +import static se.leap.bitmaskclient.appUpdate.DownloadService.UPDATE_DOWNLOADED; +import static se.leap.bitmaskclient.appUpdate.DownloadService.UPDATE_DOWNLOAD_FAILED; +import static se.leap.bitmaskclient.appUpdate.DownloadService.UPDATE_FOUND; +import static se.leap.bitmaskclient.appUpdate.DownloadService.UPDATE_NOT_FOUND; +import static se.leap.bitmaskclient.appUpdate.DownloadServiceCommand.DOWNLOAD_UPDATE; +import static se.leap.bitmaskclient.appUpdate.FileProviderUtil.getUriFor; + +public class DownloadBroadcastReceiver extends BroadcastReceiver { + + public static final String ACTION_DOWNLOAD = "se.leap.bitmaskclient.appUpdate.ACTION_DOWNLOAD"; + private static final String TAG = DownloadBroadcastReceiver.class.getSimpleName(); + + private DownloadNotificationManager notificationManager; + + + @Override + public void onReceive(Context context, Intent intent) { + Log.d(TAG, "DOWNLOAD ON RECEIVE!"); + String action = intent.getAction(); + if (action == null) { + return; + } + + if (notificationManager == null) { + notificationManager = new DownloadNotificationManager(context.getApplicationContext()); + } + + int resultCode = intent.getIntExtra(BROADCAST_RESULT_CODE, RESULT_CANCELED); + Bundle resultData = intent.getParcelableExtra(Constants.BROADCAST_RESULT_KEY); + + switch (action) { + case BROADCAST_DOWNLOAD_SERVICE_EVENT: + switch (resultCode) { + case UPDATE_FOUND: + notificationManager.buildDownloadFoundNotification(); + break; + case UPDATE_NOT_FOUND: + if (resultData.getBoolean(NO_NEW_VERISON, false)) { + //TODO: Save in preferences date, retry in a week + } else if (resultData.getBoolean(DOWNLOAD_FAILED, false)) { + Toast.makeText(context.getApplicationContext(), "Update check failed.", Toast.LENGTH_LONG).show(); + } + break; + case UPDATE_DOWNLOADED: + notificationManager.cancelNotifications(); + Intent installIntent = new Intent(Intent.ACTION_VIEW); + File update = UpdateDownloadManager.getUpdateFile(context); + if (update.exists()) { + installIntent.setDataAndType(getUriFor(context, update), APP_TYPE); + } + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + context.startActivity(installIntent); + break; + case UPDATE_DOWNLOAD_FAILED: + notificationManager.cancelNotifications(); + Toast.makeText(context.getApplicationContext(), "Update download failed.", Toast.LENGTH_LONG).show(); + break; + case DOWNLOAD_PROGRESS: + int progress = resultData.getInt(PROGRESS_VALUE, 0); + notificationManager.buildDownloadUpdateProgress(progress); + break; + } + break; + + case ACTION_DOWNLOAD: + DownloadServiceCommand.execute(context.getApplicationContext(), DOWNLOAD_UPDATE); + break; + + default: + break; + } + + } +} diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadConnector.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadConnector.java new file mode 100644 index 00000000..9427083d --- /dev/null +++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadConnector.java @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2020 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package se.leap.bitmaskclient.appUpdate; + + +import android.util.Log; +import android.util.Pair; + +import androidx.annotation.NonNull; + +import java.io.File; +import java.io.InputStream; +import java.util.List; +import java.util.Scanner; + +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okio.Buffer; +import okio.BufferedSink; +import okio.BufferedSource; +import okio.Okio; + + +/** + * This class encapsulates HTTP requests so that the results can be mocked + * and it's owning UpdateDownloadManager class logic can be unit tested properly + * + */ +public class DownloadConnector { + + private static final String TAG = DownloadConnector.class.getSimpleName(); + final static String APP_TYPE = "application/vnd.android.package-archive"; + final static String TEXT_FILE_TYPE = "application/text"; + + public interface DownloadProgress { + void onUpdate(int progress); + } + + static String requestTextFileFromServer(@NonNull String url, @NonNull OkHttpClient okHttpClient) { + try { + Request request = new Request.Builder() + .url(url) + .addHeader("Content-Type", TEXT_FILE_TYPE) + .build(); + + Response response = okHttpClient.newCall(request).execute(); + if (!response.isSuccessful()) { + return null; + } + InputStream inputStream = response.body().byteStream(); + Scanner scanner = new Scanner(inputStream).useDelimiter("\\A"); + if (scanner.hasNext()) { + return scanner.next(); + } + return null; + + } catch (Exception e) { + Log.d(TAG, "Text file download failed"); + } + + return null; + } + + static File requestFileFromServer(@NonNull String url, @NonNull OkHttpClient okHttpClient, File destFile, DownloadProgress callback) { + BufferedSink sink = null; + BufferedSource source = null; + try { + Request.Builder requestBuilder = new Request.Builder() + .url(url) + .addHeader("Content-Type", APP_TYPE); + Request request = requestBuilder.build(); + + Response response = okHttpClient.newCall(request).execute(); + ResponseBody body = response.body(); + InputStream in = body.byteStream(); + long contentLength = body.contentLength(); + source = body.source(); + sink = Okio.buffer(Okio.sink(destFile)); + Buffer sinkBuffer = sink.buffer(); + long totalBytesRead = 0; + int bufferSize = 8 * 1024; + long bytesRead; + while ((bytesRead = source.read(sinkBuffer, bufferSize)) != -1) { + sink.emit(); + totalBytesRead += bytesRead; + int progress = (int) ((totalBytesRead * 100) / contentLength); + callback.onUpdate(progress); + } + sink.flush(); + + return destFile; + + } catch (Exception e) { + Log.d(TAG, "File download failed"); + } + + return null; + } + +} diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadNotificationManager.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadNotificationManager.java new file mode 100644 index 00000000..4f7f2883 --- /dev/null +++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadNotificationManager.java @@ -0,0 +1,113 @@ +/** + * Copyright (c) 2020 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package se.leap.bitmaskclient.appUpdate; + +import android.annotation.TargetApi; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.Build; + +import androidx.annotation.NonNull; +import androidx.core.app.NotificationCompat; + +import se.leap.bitmaskclient.R; + +import static android.content.Intent.CATEGORY_DEFAULT; +import static se.leap.bitmaskclient.appUpdate.DownloadBroadcastReceiver.ACTION_DOWNLOAD; + +public class DownloadNotificationManager { + private Context context; + private final static int DOWNLOAD_NOTIFICATION_ID = 1; + + public DownloadNotificationManager(@NonNull Context context) { + this.context = context; + } + + public void buildDownloadFoundNotification() { + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + if (notificationManager == null) { + return; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + createNotificationChannel(notificationManager); + } + NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this.context, DownloadService.NOTIFICATION_CHANNEL_NEWSTATUS_ID); + notificationBuilder.setAutoCancel(true) + .setDefaults(Notification.DEFAULT_ALL) + .setWhen(System.currentTimeMillis()) + .setSmallIcon(R.mipmap.ic_launcher) + .setTicker(context.getString(R.string.version_update_title, context.getString(R.string.app_name))) + .setContentTitle(context.getString(R.string.version_update_title, context.getString(R.string.app_name))) + .setContentText(context.getString(R.string.version_update_found)) + .setContentIntent(getDownloadIntent()); + notificationManager.notify(DOWNLOAD_NOTIFICATION_ID, notificationBuilder.build()); + } + + @TargetApi(26) + private void createNotificationChannel(NotificationManager notificationManager) { + CharSequence name = "Bitmask Updates"; + String description = "Informs about available updates"; + NotificationChannel channel = new NotificationChannel(DownloadService.NOTIFICATION_CHANNEL_NEWSTATUS_ID, + name, + NotificationManager.IMPORTANCE_LOW); + channel.setSound(null, null); + channel.setDescription(description); + // Register the channel with the system; you can't change the importance + // or other notification behaviors after this + notificationManager.createNotificationChannel(channel); + } + + + + private PendingIntent getDownloadIntent() { + Intent downloadIntent = new Intent(context, DownloadBroadcastReceiver.class); + downloadIntent.setAction(ACTION_DOWNLOAD); + return PendingIntent.getBroadcast(context, 0, downloadIntent, PendingIntent.FLAG_CANCEL_CURRENT); + } + + public void buildDownloadUpdateProgress(int progress) { + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + if (notificationManager == null) { + return; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + createNotificationChannel(notificationManager); + } + NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this.context, DownloadService.NOTIFICATION_CHANNEL_NEWSTATUS_ID); + notificationBuilder.setAutoCancel(true) + .setDefaults(Notification.DEFAULT_ALL) + .setAutoCancel(false) + .setOngoing(true) + .setSmallIcon(R.mipmap.ic_launcher) + .setContentTitle(context.getString(R.string.version_update_apk_description, context.getString(R.string.app_name))) + .setProgress(100, progress, false) + .setContentIntent(getDownloadIntent()); + notificationManager.notify(DOWNLOAD_NOTIFICATION_ID, notificationBuilder.build()); + } + + public void cancelNotifications() { + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + if (notificationManager == null) { + return; + } + notificationManager.cancel(DOWNLOAD_NOTIFICATION_ID); + } +} diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadService.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadService.java new file mode 100644 index 00000000..bc9adfc1 --- /dev/null +++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadService.java @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2020 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package se.leap.bitmaskclient.appUpdate; + +import android.content.Context; +import android.content.Intent; + +import androidx.annotation.NonNull; +import androidx.core.app.JobIntentService; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import se.leap.bitmaskclient.OkHttpClientGenerator; + +public class DownloadService extends JobIntentService implements UpdateDownloadManager.DownloadServiceCallback { + + static final int JOB_ID = 161376; + static final String NOTIFICATION_CHANNEL_NEWSTATUS_ID = "bitmask_download_service_news"; + + final public static String TAG = DownloadService.class.getSimpleName(), + PROGRESS_VALUE = "progressValue", + NO_NEW_VERISON = "noNewVersion", + DOWNLOAD_FAILED = "downloadFailed", + NO_PUB_KEY = "noPubKey", + VERIFICATION_ERROR = "verificationError"; + + final public static int + UPDATE_DOWNLOADED = 1, + UPDATE_DOWNLOAD_FAILED = 2, + UPDATE_FOUND = 3, + UPDATE_NOT_FOUND = 4, + DOWNLOAD_PROGRESS = 6; + + + private UpdateDownloadManager updateDownloadManager; + + + @Override + public void onCreate() { + super.onCreate(); + updateDownloadManager = initDownloadManager(); + } + + @Override + protected void onHandleWork(@NonNull Intent intent) { + updateDownloadManager.handleIntent(intent); + } + + /** + * Convenience method for enqueuing work in to this service. + */ + static void enqueueWork(Context context, Intent work) { + try { + DownloadService.enqueueWork(context, DownloadService.class, JOB_ID, work); + } catch (IllegalStateException e) { + e.printStackTrace(); + } + } + + private UpdateDownloadManager initDownloadManager() { + OkHttpClientGenerator clientGenerator = new OkHttpClientGenerator(null); + return new UpdateDownloadManager(this, clientGenerator, this); + } + + @Override + public void broadcastEvent(Intent intent) { + LocalBroadcastManager.getInstance(this).sendBroadcast(intent); + } +} diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadServiceCommand.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadServiceCommand.java new file mode 100644 index 00000000..c4e809f2 --- /dev/null +++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadServiceCommand.java @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2020 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package se.leap.bitmaskclient.appUpdate; + +import android.content.Context; +import android.content.Intent; +import android.os.ResultReceiver; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import se.leap.bitmaskclient.ProviderAPI; + +public class DownloadServiceCommand { + + public final static String + CHECK_VERSION_FILE = "checkVersionFile", + DOWNLOAD_UPDATE = "downloadUpdate"; + + private Context context; + private String action; + private ResultReceiver resultReceiver; + + private DownloadServiceCommand(@NotNull Context context, @NotNull String action) { + this(context.getApplicationContext(), action, null); + } + + private DownloadServiceCommand(@NotNull Context context, @NotNull String action, @Nullable ResultReceiver resultReceiver) { + super(); + this.context = context; + this.action = action; + this.resultReceiver = resultReceiver; + } + + + private Intent setUpIntent() { + Intent command = new Intent(context, ProviderAPI.class); + command.setAction(action); + if (resultReceiver != null) { + command.putExtra(ProviderAPI.RECEIVER_KEY, resultReceiver); + } + return command; + } + + private boolean isInitialized() { + return context != null; + } + + + private void execute() { + if (isInitialized()) { + Intent intent = setUpIntent(); + DownloadService.enqueueWork(context, intent); + } + } + + public static void execute(Context context, String action) { + DownloadServiceCommand command = new DownloadServiceCommand(context, action); + command.execute(); + } + + public static void execute(Context context, String action, ResultReceiver resultReceiver) { + DownloadServiceCommand command = new DownloadServiceCommand(context, action, resultReceiver); + command.execute(); + } + +} diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/FileProviderUtil.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/FileProviderUtil.java new file mode 100644 index 00000000..756a3b99 --- /dev/null +++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/FileProviderUtil.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2020 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package se.leap.bitmaskclient.appUpdate; + +import android.content.Context; +import android.net.Uri; +import android.os.Build; +import androidx.annotation.NonNull; +import androidx.core.content.FileProvider; + +import java.io.File; + +import se.leap.bitmaskclient.BuildConfig; + +/** + * From Signal + */ + +public class FileProviderUtil { + + private static final String AUTHORITY = BuildConfig.APPLICATION_ID +".fileprovider"; + + public static Uri getUriFor(@NonNull Context context, @NonNull File file) { + if (Build.VERSION.SDK_INT >= 24) return FileProvider.getUriForFile(context, AUTHORITY, file); + else return Uri.fromFile(file); + } + + public static boolean isAuthority(@NonNull Uri uri) { + return AUTHORITY.equals(uri.getAuthority()); + } + + public static boolean delete(@NonNull Context context, @NonNull Uri uri) { + if (AUTHORITY.equals(uri.getAuthority())) { + return context.getContentResolver().delete(uri, null, null) > 0; + } + return new File(uri.getPath()).delete(); + } +} diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/UpdateDownloadManager.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/UpdateDownloadManager.java new file mode 100644 index 00000000..698a0d17 --- /dev/null +++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/UpdateDownloadManager.java @@ -0,0 +1,231 @@ +/** + * Copyright (c) 2020 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package se.leap.bitmaskclient.appUpdate; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.ResultReceiver; +import android.util.Log; + +import androidx.annotation.NonNull; + +import java.io.File; + +import okhttp3.OkHttpClient; +import pgpverify.Logger; +import pgpverify.PgpVerifier; +import se.leap.bitmaskclient.BuildConfig; +import se.leap.bitmaskclient.OkHttpClientGenerator; +import se.leap.bitmaskclient.R; + +import static android.text.TextUtils.isEmpty; +import static se.leap.bitmaskclient.Constants.BROADCAST_DOWNLOAD_SERVICE_EVENT; +import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_CODE; +import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY; +import static se.leap.bitmaskclient.ProviderAPI.RECEIVER_KEY; +import static se.leap.bitmaskclient.appUpdate.DownloadService.DOWNLOAD_FAILED; +import static se.leap.bitmaskclient.appUpdate.DownloadService.DOWNLOAD_PROGRESS; +import static se.leap.bitmaskclient.appUpdate.DownloadService.NO_NEW_VERISON; +import static se.leap.bitmaskclient.appUpdate.DownloadService.NO_PUB_KEY; +import static se.leap.bitmaskclient.appUpdate.DownloadService.PROGRESS_VALUE; +import static se.leap.bitmaskclient.appUpdate.DownloadService.UPDATE_DOWNLOADED; +import static se.leap.bitmaskclient.appUpdate.DownloadService.UPDATE_DOWNLOAD_FAILED; +import static se.leap.bitmaskclient.appUpdate.DownloadService.UPDATE_FOUND; +import static se.leap.bitmaskclient.appUpdate.DownloadService.UPDATE_NOT_FOUND; +import static se.leap.bitmaskclient.appUpdate.DownloadService.VERIFICATION_ERROR; +import static se.leap.bitmaskclient.appUpdate.DownloadServiceCommand.CHECK_VERSION_FILE; +import static se.leap.bitmaskclient.appUpdate.DownloadServiceCommand.DOWNLOAD_UPDATE; +import static se.leap.bitmaskclient.utils.FileHelper.readPublicKey; + +public class UpdateDownloadManager implements Logger, DownloadConnector.DownloadProgress { + + + private static final String TAG = UpdateDownloadManager.class.getSimpleName(); + + public interface DownloadServiceCallback { + void broadcastEvent(Intent intent); + } + + private Context context; + + private PgpVerifier pgpVerifier; + private DownloadServiceCallback serviceCallback; + OkHttpClientGenerator clientGenerator; + + + public UpdateDownloadManager(Context context, OkHttpClientGenerator clientGenerator, DownloadServiceCallback callback) { + this.context = context; + this.clientGenerator = clientGenerator; + pgpVerifier = new PgpVerifier(); + pgpVerifier.setLogger(this); + serviceCallback = callback; + } + + //pgpverify Logger interface + @Override + public void log(String s) { + + } + + @Override + public void onUpdate(int progress) { + Bundle resultData = new Bundle(); + resultData.putInt(PROGRESS_VALUE, progress); + broadcastEvent(DOWNLOAD_PROGRESS, resultData); + } + + public void handleIntent(Intent command) { + ResultReceiver receiver = null; + if (command.getParcelableExtra(RECEIVER_KEY) != null) { + receiver = command.getParcelableExtra(RECEIVER_KEY); + } + String action = command.getAction(); + + Bundle result = new Bundle(); + switch (action) { + case CHECK_VERSION_FILE: + result = checkVersionFile(result); + if (result.getBoolean(BROADCAST_RESULT_KEY)) { + sendToReceiverOrBroadcast(receiver, UPDATE_FOUND, result); + } else { + sendToReceiverOrBroadcast(receiver, UPDATE_NOT_FOUND, result); + } + break; + case DOWNLOAD_UPDATE: + result = downloadUpdate(result); + if (result.getBoolean(BROADCAST_RESULT_KEY)) { + sendToReceiverOrBroadcast(receiver, UPDATE_DOWNLOADED, result); + } else { + sendToReceiverOrBroadcast(receiver, UPDATE_DOWNLOAD_FAILED, result); + } + break; + + } + } + + public static File getUpdateFile(Context context) { + return new File(context.getExternalFilesDir(null) + "/" + context.getString(R.string.app_name) + "_update.apk"); + } + + private Bundle downloadUpdate(Bundle task) { + + String publicKey = readPublicKey(context); + if (isEmpty(publicKey)) { + task.putBoolean(BROADCAST_RESULT_KEY, false); + task.putBoolean(NO_PUB_KEY, true); + return task; + } + + OkHttpClient client = clientGenerator.init(); + String signature = DownloadConnector.requestTextFileFromServer(BuildConfig.signature_url, client); + if (signature == null) { + task.putBoolean(BROADCAST_RESULT_KEY, false); + task.putBoolean(DOWNLOAD_FAILED, true); + return task; + } + + File destinationFile = getUpdateFile(context); + if (destinationFile.exists()) { + destinationFile.delete(); + } + + destinationFile = DownloadConnector.requestFileFromServer(BuildConfig.update_apk_url, client, destinationFile, this); + + if (destinationFile == null) { + task.putBoolean(BROADCAST_RESULT_KEY, false); + task.putBoolean(DOWNLOAD_FAILED, true); + return task; + } + + boolean successfulVerified = pgpVerifier.verify(signature, publicKey, destinationFile.getAbsolutePath()); + if (!successfulVerified) { + destinationFile.delete(); + task.putBoolean(BROADCAST_RESULT_KEY, false); + task.putBoolean(VERIFICATION_ERROR, true); + return task; + } + + task.putBoolean(BROADCAST_RESULT_KEY, true); + return task; + } + + private static void clearPreviousDownloads(@NonNull Context context, String destinationFile) { + File directory = context.getExternalFilesDir(null); + + if (directory == null) { + Log.w(TAG, "Failed to read external files directory."); + return; + } + + for (File file : directory.listFiles()) { + if (file.getName().equals(destinationFile)) { + if (file.delete()) { + Log.d(TAG, "Deleted " + file.getName()); + } + } + } + } + + private Bundle checkVersionFile(Bundle task) { + OkHttpClient client = clientGenerator.init(); + String versionString = DownloadConnector.requestTextFileFromServer(BuildConfig.version_file_url, client); + + if (versionString != null) { + versionString = versionString.replace("\n", "").trim(); + } + + int version = -1; + try { + version = Integer.valueOf(versionString); + } catch (NumberFormatException e) { + e.printStackTrace(); + Log.e(TAG, "could not parse version code: " + versionString); + } + + if (version == -1) { + task.putBoolean(BROADCAST_RESULT_KEY, false); + task.putBoolean(DOWNLOAD_FAILED, true); + } else if (BuildConfig.VERSION_CODE >= version) { + task.putBoolean(BROADCAST_RESULT_KEY, false); + task.putBoolean(NO_NEW_VERISON, true); + } else { + task.putBoolean(BROADCAST_RESULT_KEY, true); + } + return task; + } + + private void sendToReceiverOrBroadcast(ResultReceiver receiver, int resultCode, Bundle resultData) { + if (resultData == null || resultData == Bundle.EMPTY) { + resultData = new Bundle(); + } + if (receiver != null) { + receiver.send(resultCode, resultData); + } else { + broadcastEvent(resultCode, resultData); + } + } + + private void broadcastEvent(int resultCode , Bundle resultData) { + Intent intentUpdate = new Intent(BROADCAST_DOWNLOAD_SERVICE_EVENT); + intentUpdate.addCategory(Intent.CATEGORY_DEFAULT); + intentUpdate.putExtra(BROADCAST_RESULT_CODE, resultCode); + intentUpdate.putExtra(BROADCAST_RESULT_KEY, resultData); + serviceCallback.broadcastEvent(intentUpdate); + } + +} -- cgit v1.2.3 From 95554c3eb064114f9a8d494aec7ba05e8cd3aaf1 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Mon, 28 Dec 2020 00:06:00 +0100 Subject: improve invisible activity theme --- .../fatweb/java/se.leap.bitmaskclient/appUpdate/InstallActivity.java | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/InstallActivity.java (limited to 'app/src/fatweb/java/se.leap.bitmaskclient') diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/InstallActivity.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/InstallActivity.java new file mode 100644 index 00000000..e373a888 --- /dev/null +++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/InstallActivity.java @@ -0,0 +1,4 @@ +package se.leap.bitmaskclient.appUpdate; + +public class InstallActivity { +} -- cgit v1.2.3 From c008a935f92b79cb7b6f649fc876d398e20ebb22 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Mon, 28 Dec 2020 00:47:19 +0100 Subject: download apk, request permission and install app update --- .../appUpdate/DownloadBroadcastReceiver.java | 32 ++++---- .../appUpdate/DownloadConnector.java | 8 +- .../appUpdate/DownloadNotificationManager.java | 86 +++++++++++++------- .../appUpdate/InstallActivity.java | 94 +++++++++++++++++++++- 4 files changed, 170 insertions(+), 50 deletions(-) (limited to 'app/src/fatweb/java/se.leap.bitmaskclient') diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadBroadcastReceiver.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadBroadcastReceiver.java index 6613d394..f410b9ff 100644 --- a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadBroadcastReceiver.java +++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadBroadcastReceiver.java @@ -19,7 +19,6 @@ package se.leap.bitmaskclient.appUpdate; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.net.Uri; import android.os.Bundle; import android.util.Log; import android.widget.Toast; @@ -27,12 +26,12 @@ import android.widget.Toast; import java.io.File; import se.leap.bitmaskclient.Constants; +import se.leap.bitmaskclient.R; +import se.leap.bitmaskclient.utils.ConfigHelper; import static android.app.Activity.RESULT_CANCELED; import static se.leap.bitmaskclient.Constants.BROADCAST_DOWNLOAD_SERVICE_EVENT; import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_CODE; -import static se.leap.bitmaskclient.appUpdate.DownloadConnector.APP_TYPE; -import static se.leap.bitmaskclient.appUpdate.DownloadService.DOWNLOAD_FAILED; import static se.leap.bitmaskclient.appUpdate.DownloadService.DOWNLOAD_PROGRESS; import static se.leap.bitmaskclient.appUpdate.DownloadService.NO_NEW_VERISON; import static se.leap.bitmaskclient.appUpdate.DownloadService.PROGRESS_VALUE; @@ -40,8 +39,9 @@ import static se.leap.bitmaskclient.appUpdate.DownloadService.UPDATE_DOWNLOADED; import static se.leap.bitmaskclient.appUpdate.DownloadService.UPDATE_DOWNLOAD_FAILED; import static se.leap.bitmaskclient.appUpdate.DownloadService.UPDATE_FOUND; import static se.leap.bitmaskclient.appUpdate.DownloadService.UPDATE_NOT_FOUND; +import static se.leap.bitmaskclient.appUpdate.DownloadService.VERIFICATION_ERROR; import static se.leap.bitmaskclient.appUpdate.DownloadServiceCommand.DOWNLOAD_UPDATE; -import static se.leap.bitmaskclient.appUpdate.FileProviderUtil.getUriFor; +import static se.leap.bitmaskclient.appUpdate.UpdateDownloadManager.getUpdateFile; public class DownloadBroadcastReceiver extends BroadcastReceiver { @@ -75,25 +75,21 @@ public class DownloadBroadcastReceiver extends BroadcastReceiver { case UPDATE_NOT_FOUND: if (resultData.getBoolean(NO_NEW_VERISON, false)) { //TODO: Save in preferences date, retry in a week - } else if (resultData.getBoolean(DOWNLOAD_FAILED, false)) { - Toast.makeText(context.getApplicationContext(), "Update check failed.", Toast.LENGTH_LONG).show(); } break; case UPDATE_DOWNLOADED: - notificationManager.cancelNotifications(); - Intent installIntent = new Intent(Intent.ACTION_VIEW); - File update = UpdateDownloadManager.getUpdateFile(context); - if (update.exists()) { - installIntent.setDataAndType(getUriFor(context, update), APP_TYPE); - } - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true); - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - context.startActivity(installIntent); + notificationManager.buildDownloadSuccessfulNotification(); break; case UPDATE_DOWNLOAD_FAILED: - notificationManager.cancelNotifications(); - Toast.makeText(context.getApplicationContext(), "Update download failed.", Toast.LENGTH_LONG).show(); + if (resultData.getBoolean(VERIFICATION_ERROR, false)) { + Toast.makeText(context.getApplicationContext(), context.getString(R.string.version_update_error_pgp_verification), Toast.LENGTH_LONG).show(); + } else { + Toast.makeText(context.getApplicationContext(), context.getString(R.string.version_update_error), Toast.LENGTH_LONG).show(); + } + File file = getUpdateFile(context); + if (file.exists()) { + file.delete(); + } break; case DOWNLOAD_PROGRESS: int progress = resultData.getInt(PROGRESS_VALUE, 0); diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadConnector.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadConnector.java index 9427083d..d4c5de7f 100644 --- a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadConnector.java +++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadConnector.java @@ -45,8 +45,8 @@ import okio.Okio; public class DownloadConnector { private static final String TAG = DownloadConnector.class.getSimpleName(); - final static String APP_TYPE = "application/vnd.android.package-archive"; - final static String TEXT_FILE_TYPE = "application/text"; + public final static String APP_TYPE = "application/vnd.android.package-archive"; + public final static String TEXT_FILE_TYPE = "application/text"; public interface DownloadProgress { void onUpdate(int progress); @@ -78,8 +78,8 @@ public class DownloadConnector { } static File requestFileFromServer(@NonNull String url, @NonNull OkHttpClient okHttpClient, File destFile, DownloadProgress callback) { - BufferedSink sink = null; - BufferedSource source = null; + BufferedSink sink; + BufferedSource source; try { Request.Builder requestBuilder = new Request.Builder() .url(url) diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadNotificationManager.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadNotificationManager.java index 4f7f2883..aaf487aa 100644 --- a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadNotificationManager.java +++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadNotificationManager.java @@ -42,18 +42,14 @@ public class DownloadNotificationManager { } public void buildDownloadFoundNotification() { - NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + NotificationManager notificationManager = initNotificationManager(); if (notificationManager == null) { return; } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - createNotificationChannel(notificationManager); - } - NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this.context, DownloadService.NOTIFICATION_CHANNEL_NEWSTATUS_ID); - notificationBuilder.setAutoCancel(true) - .setDefaults(Notification.DEFAULT_ALL) + NotificationCompat.Builder notificationBuilder = initNotificationBuilderDefaults(); + notificationBuilder + .setSmallIcon(R.drawable.ic_about_36) .setWhen(System.currentTimeMillis()) - .setSmallIcon(R.mipmap.ic_launcher) .setTicker(context.getString(R.string.version_update_title, context.getString(R.string.app_name))) .setContentTitle(context.getString(R.string.version_update_title, context.getString(R.string.app_name))) .setContentText(context.getString(R.string.version_update_found)) @@ -61,6 +57,51 @@ public class DownloadNotificationManager { notificationManager.notify(DOWNLOAD_NOTIFICATION_ID, notificationBuilder.build()); } + public void buildDownloadSuccessfulNotification() { + NotificationManager notificationManager = initNotificationManager(); + if (notificationManager == null) { + return; + } + NotificationCompat.Builder notificationBuilder = initNotificationBuilderDefaults(); + notificationBuilder + .setSmallIcon(android.R.drawable.stat_sys_download_done) + .setWhen(System.currentTimeMillis()) + .setTicker(context.getString(R.string.version_update_title, context.getString(R.string.app_name))) + .setContentTitle(context.getString(R.string.version_update_download_title, context.getString(R.string.app_name))) + .setContentText(context.getString(R.string.version_update_download_description)) + .setContentIntent(getInstallIntent()); + notificationManager.notify(DOWNLOAD_NOTIFICATION_ID, notificationBuilder.build()); + } + + public void buildDownloadUpdateProgress(int progress) { + NotificationManager notificationManager = initNotificationManager(); + if (notificationManager == null) { + return; + } + + NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this.context, DownloadService.NOTIFICATION_CHANNEL_NEWSTATUS_ID); + notificationBuilder + .setDefaults(Notification.DEFAULT_ALL) + .setAutoCancel(false) + .setOngoing(true) + .setSmallIcon(android.R.drawable.stat_sys_download) + .setContentTitle(context.getString(R.string.version_update_apk_description, context.getString(R.string.app_name))) + .setProgress(100, progress, false) + .setContentIntent(getDownloadIntent()); + notificationManager.notify(DOWNLOAD_NOTIFICATION_ID, notificationBuilder.build()); + } + + private NotificationManager initNotificationManager() { + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + if (notificationManager == null) { + return null; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + createNotificationChannel(notificationManager); + } + return notificationManager; + } + @TargetApi(26) private void createNotificationChannel(NotificationManager notificationManager) { CharSequence name = "Bitmask Updates"; @@ -75,7 +116,13 @@ public class DownloadNotificationManager { notificationManager.createNotificationChannel(channel); } - + private NotificationCompat.Builder initNotificationBuilderDefaults() { + NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this.context, DownloadService.NOTIFICATION_CHANNEL_NEWSTATUS_ID); + notificationBuilder. + setDefaults(Notification.DEFAULT_ALL). + setAutoCancel(true); + return notificationBuilder; + } private PendingIntent getDownloadIntent() { Intent downloadIntent = new Intent(context, DownloadBroadcastReceiver.class); @@ -83,24 +130,9 @@ public class DownloadNotificationManager { return PendingIntent.getBroadcast(context, 0, downloadIntent, PendingIntent.FLAG_CANCEL_CURRENT); } - public void buildDownloadUpdateProgress(int progress) { - NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - if (notificationManager == null) { - return; - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - createNotificationChannel(notificationManager); - } - NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this.context, DownloadService.NOTIFICATION_CHANNEL_NEWSTATUS_ID); - notificationBuilder.setAutoCancel(true) - .setDefaults(Notification.DEFAULT_ALL) - .setAutoCancel(false) - .setOngoing(true) - .setSmallIcon(R.mipmap.ic_launcher) - .setContentTitle(context.getString(R.string.version_update_apk_description, context.getString(R.string.app_name))) - .setProgress(100, progress, false) - .setContentIntent(getDownloadIntent()); - notificationManager.notify(DOWNLOAD_NOTIFICATION_ID, notificationBuilder.build()); + private PendingIntent getInstallIntent() { + Intent installIntent = new Intent(context, InstallActivity.class); + return PendingIntent.getActivity(context, 0, installIntent, PendingIntent.FLAG_CANCEL_CURRENT); } public void cancelNotifications() { diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/InstallActivity.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/InstallActivity.java index e373a888..f2f82238 100644 --- a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/InstallActivity.java +++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/InstallActivity.java @@ -1,4 +1,96 @@ +/** + * Copyright (c) 2020 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package se.leap.bitmaskclient.appUpdate; -public class InstallActivity { +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.provider.Settings; +import android.widget.Toast; + +import androidx.annotation.Nullable; + +import java.io.File; + +import de.blinkt.openvpn.core.VpnStatus; +import se.leap.bitmaskclient.R; +import se.leap.bitmaskclient.utils.PreferenceHelper; + +import static se.leap.bitmaskclient.Constants.REQUEST_CODE_INSTALL_UPDATE; +import static se.leap.bitmaskclient.Constants.REQUEST_CODE_REQUEST_UPDATE; +import static se.leap.bitmaskclient.appUpdate.DownloadConnector.APP_TYPE; +import static se.leap.bitmaskclient.appUpdate.FileProviderUtil.getUriFor; + +public class InstallActivity extends Activity { + + private static final String TAG = InstallActivity.class.getSimpleName(); + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestPermissionAndInstall(); + } + + + private void requestPermissionAndInstall() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !this.getPackageManager().canRequestPackageInstalls()) { + startActivityForResult(new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, Uri.parse("package:"+getPackageName())), + REQUEST_CODE_REQUEST_UPDATE); + } else { + installUpdate(); + } + } + + protected void installUpdate() { + PreferenceHelper.restartOnUpdate(this.getApplicationContext(), true); + + Intent installIntent = new Intent(Intent.ACTION_VIEW); + File update = UpdateDownloadManager.getUpdateFile(this.getApplicationContext()); + if (update.exists()) { + installIntent.setDataAndType(getUriFor(this.getApplicationContext(), update), APP_TYPE); + installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + this.startActivityForResult(installIntent, REQUEST_CODE_INSTALL_UPDATE); + } + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == REQUEST_CODE_REQUEST_UPDATE) { + if (resultCode == RESULT_OK) { + installUpdate(); + } else { + Toast.makeText(this, getString(R.string.version_update_error_permissions), Toast.LENGTH_LONG).show(); + finish(); + } + } else if (requestCode == REQUEST_CODE_INSTALL_UPDATE) { + switch (resultCode) { + case RESULT_OK: + Toast.makeText(this, "Update successful.", Toast.LENGTH_LONG).show(); + break; + case RESULT_CANCELED: + case RESULT_FIRST_USER: + Toast.makeText(this, getString(R.string.version_update_error), Toast.LENGTH_LONG).show(); + break; + } + finish(); + } + } } -- cgit v1.2.3 From 10a6c27ab03189cf5c504d09affedb309d1dafe6 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Mon, 28 Dec 2020 16:08:48 +0100 Subject: check once a week for an app update --- .../se.leap.bitmaskclient/appUpdate/DownloadBroadcastReceiver.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'app/src/fatweb/java/se.leap.bitmaskclient') diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadBroadcastReceiver.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadBroadcastReceiver.java index f410b9ff..4719737b 100644 --- a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadBroadcastReceiver.java +++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadBroadcastReceiver.java @@ -20,14 +20,13 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Bundle; -import android.util.Log; import android.widget.Toast; import java.io.File; import se.leap.bitmaskclient.Constants; import se.leap.bitmaskclient.R; -import se.leap.bitmaskclient.utils.ConfigHelper; +import se.leap.bitmaskclient.utils.PreferenceHelper; import static android.app.Activity.RESULT_CANCELED; import static se.leap.bitmaskclient.Constants.BROADCAST_DOWNLOAD_SERVICE_EVENT; @@ -53,7 +52,6 @@ public class DownloadBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - Log.d(TAG, "DOWNLOAD ON RECEIVE!"); String action = intent.getAction(); if (action == null) { return; @@ -74,7 +72,7 @@ public class DownloadBroadcastReceiver extends BroadcastReceiver { break; case UPDATE_NOT_FOUND: if (resultData.getBoolean(NO_NEW_VERISON, false)) { - //TODO: Save in preferences date, retry in a week + PreferenceHelper.setLastAppUpdateCheck(context.getApplicationContext()); } break; case UPDATE_DOWNLOADED: -- cgit v1.2.3 From 09865483f39e01a52e401baba280260df2c2e766 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Mon, 28 Dec 2020 16:12:01 +0100 Subject: cleanup, remove unused code and deprecated comments --- .../appUpdate/DownloadConnector.java | 1 - .../appUpdate/FileProviderUtil.java | 15 --------------- .../appUpdate/InstallActivity.java | 1 - .../appUpdate/UpdateDownloadManager.java | 20 -------------------- 4 files changed, 37 deletions(-) (limited to 'app/src/fatweb/java/se.leap.bitmaskclient') diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadConnector.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadConnector.java index d4c5de7f..f081331f 100644 --- a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadConnector.java +++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadConnector.java @@ -88,7 +88,6 @@ public class DownloadConnector { Response response = okHttpClient.newCall(request).execute(); ResponseBody body = response.body(); - InputStream in = body.byteStream(); long contentLength = body.contentLength(); source = body.source(); sink = Okio.buffer(Okio.sink(destFile)); diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/FileProviderUtil.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/FileProviderUtil.java index 756a3b99..4966a863 100644 --- a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/FileProviderUtil.java +++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/FileProviderUtil.java @@ -26,10 +26,6 @@ import java.io.File; import se.leap.bitmaskclient.BuildConfig; -/** - * From Signal - */ - public class FileProviderUtil { private static final String AUTHORITY = BuildConfig.APPLICATION_ID +".fileprovider"; @@ -38,15 +34,4 @@ public class FileProviderUtil { if (Build.VERSION.SDK_INT >= 24) return FileProvider.getUriForFile(context, AUTHORITY, file); else return Uri.fromFile(file); } - - public static boolean isAuthority(@NonNull Uri uri) { - return AUTHORITY.equals(uri.getAuthority()); - } - - public static boolean delete(@NonNull Context context, @NonNull Uri uri) { - if (AUTHORITY.equals(uri.getAuthority())) { - return context.getContentResolver().delete(uri, null, null) > 0; - } - return new File(uri.getPath()).delete(); - } } diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/InstallActivity.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/InstallActivity.java index f2f82238..028f47bc 100644 --- a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/InstallActivity.java +++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/InstallActivity.java @@ -28,7 +28,6 @@ import androidx.annotation.Nullable; import java.io.File; -import de.blinkt.openvpn.core.VpnStatus; import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.utils.PreferenceHelper; diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/UpdateDownloadManager.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/UpdateDownloadManager.java index 698a0d17..f2eabc7b 100644 --- a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/UpdateDownloadManager.java +++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/UpdateDownloadManager.java @@ -22,8 +22,6 @@ import android.os.Bundle; import android.os.ResultReceiver; import android.util.Log; -import androidx.annotation.NonNull; - import java.io.File; import okhttp3.OkHttpClient; @@ -114,7 +112,6 @@ public class UpdateDownloadManager implements Logger, DownloadConnector.Download sendToReceiverOrBroadcast(receiver, UPDATE_DOWNLOAD_FAILED, result); } break; - } } @@ -164,23 +161,6 @@ public class UpdateDownloadManager implements Logger, DownloadConnector.Download return task; } - private static void clearPreviousDownloads(@NonNull Context context, String destinationFile) { - File directory = context.getExternalFilesDir(null); - - if (directory == null) { - Log.w(TAG, "Failed to read external files directory."); - return; - } - - for (File file : directory.listFiles()) { - if (file.getName().equals(destinationFile)) { - if (file.delete()) { - Log.d(TAG, "Deleted " + file.getName()); - } - } - } - } - private Bundle checkVersionFile(Bundle task) { OkHttpClient client = clientGenerator.init(); String versionString = DownloadConnector.requestTextFileFromServer(BuildConfig.version_file_url, client); -- cgit v1.2.3 From 0ce7baa3ce5138d9b298b67cfcd03d04ba6a66c0 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Mon, 28 Dec 2020 16:12:51 +0100 Subject: fix lint issue, use parseInt instead of valueOf for string to integer conversion --- .../java/se.leap.bitmaskclient/appUpdate/UpdateDownloadManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/src/fatweb/java/se.leap.bitmaskclient') diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/UpdateDownloadManager.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/UpdateDownloadManager.java index f2eabc7b..b79c2a91 100644 --- a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/UpdateDownloadManager.java +++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/UpdateDownloadManager.java @@ -171,7 +171,7 @@ public class UpdateDownloadManager implements Logger, DownloadConnector.Download int version = -1; try { - version = Integer.valueOf(versionString); + version = Integer.parseInt(versionString); } catch (NumberFormatException e) { e.printStackTrace(); Log.e(TAG, "could not parse version code: " + versionString); -- cgit v1.2.3 From a41c9876bac7c769a876d5aebc3e797c7c2e73a4 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Mon, 28 Dec 2020 16:13:52 +0100 Subject: debounce notification creation during app download --- .../java/se.leap.bitmaskclient/appUpdate/DownloadConnector.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'app/src/fatweb/java/se.leap.bitmaskclient') diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadConnector.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadConnector.java index f081331f..8723f515 100644 --- a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadConnector.java +++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadConnector.java @@ -95,11 +95,16 @@ public class DownloadConnector { long totalBytesRead = 0; int bufferSize = 8 * 1024; long bytesRead; + int lastProgress = 0; while ((bytesRead = source.read(sinkBuffer, bufferSize)) != -1) { sink.emit(); totalBytesRead += bytesRead; int progress = (int) ((totalBytesRead * 100) / contentLength); - callback.onUpdate(progress); + // debouncing callbacks + if (lastProgress < progress) { + lastProgress = progress; + callback.onUpdate(progress); + } } sink.flush(); -- cgit v1.2.3 From 00f449b80ea1704d1772e580e4ce16beac5039bf Mon Sep 17 00:00:00 2001 From: cyBerta Date: Mon, 28 Dec 2020 16:14:49 +0100 Subject: remove redundant check for file deletion if app update failed --- .../se.leap.bitmaskclient/appUpdate/DownloadBroadcastReceiver.java | 7 ------- 1 file changed, 7 deletions(-) (limited to 'app/src/fatweb/java/se.leap.bitmaskclient') diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadBroadcastReceiver.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadBroadcastReceiver.java index 4719737b..525c3c9b 100644 --- a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadBroadcastReceiver.java +++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadBroadcastReceiver.java @@ -22,8 +22,6 @@ import android.content.Intent; import android.os.Bundle; import android.widget.Toast; -import java.io.File; - import se.leap.bitmaskclient.Constants; import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.utils.PreferenceHelper; @@ -40,7 +38,6 @@ import static se.leap.bitmaskclient.appUpdate.DownloadService.UPDATE_FOUND; import static se.leap.bitmaskclient.appUpdate.DownloadService.UPDATE_NOT_FOUND; import static se.leap.bitmaskclient.appUpdate.DownloadService.VERIFICATION_ERROR; import static se.leap.bitmaskclient.appUpdate.DownloadServiceCommand.DOWNLOAD_UPDATE; -import static se.leap.bitmaskclient.appUpdate.UpdateDownloadManager.getUpdateFile; public class DownloadBroadcastReceiver extends BroadcastReceiver { @@ -84,10 +81,6 @@ public class DownloadBroadcastReceiver extends BroadcastReceiver { } else { Toast.makeText(context.getApplicationContext(), context.getString(R.string.version_update_error), Toast.LENGTH_LONG).show(); } - File file = getUpdateFile(context); - if (file.exists()) { - file.delete(); - } break; case DOWNLOAD_PROGRESS: int progress = resultData.getInt(PROGRESS_VALUE, 0); -- cgit v1.2.3 From 73efd90a7bb170481ae827e8ae0523daed992b40 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Mon, 28 Dec 2020 18:43:33 +0100 Subject: no need to use onActivityResult in InstallActivity during installation it won't be called --- .../se.leap.bitmaskclient/appUpdate/InstallActivity.java | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) (limited to 'app/src/fatweb/java/se.leap.bitmaskclient') diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/InstallActivity.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/InstallActivity.java index 028f47bc..6629425c 100644 --- a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/InstallActivity.java +++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/InstallActivity.java @@ -31,7 +31,6 @@ import java.io.File; import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.utils.PreferenceHelper; -import static se.leap.bitmaskclient.Constants.REQUEST_CODE_INSTALL_UPDATE; import static se.leap.bitmaskclient.Constants.REQUEST_CODE_REQUEST_UPDATE; import static se.leap.bitmaskclient.appUpdate.DownloadConnector.APP_TYPE; import static se.leap.bitmaskclient.appUpdate.FileProviderUtil.getUriFor; @@ -46,7 +45,6 @@ public class InstallActivity extends Activity { requestPermissionAndInstall(); } - private void requestPermissionAndInstall() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !this.getPackageManager().canRequestPackageInstalls()) { startActivityForResult(new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, Uri.parse("package:"+getPackageName())), @@ -65,7 +63,8 @@ public class InstallActivity extends Activity { installIntent.setDataAndType(getUriFor(this.getApplicationContext(), update), APP_TYPE); installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - this.startActivityForResult(installIntent, REQUEST_CODE_INSTALL_UPDATE); + this.startActivity(installIntent); + finish(); } } @@ -79,17 +78,6 @@ public class InstallActivity extends Activity { Toast.makeText(this, getString(R.string.version_update_error_permissions), Toast.LENGTH_LONG).show(); finish(); } - } else if (requestCode == REQUEST_CODE_INSTALL_UPDATE) { - switch (resultCode) { - case RESULT_OK: - Toast.makeText(this, "Update successful.", Toast.LENGTH_LONG).show(); - break; - case RESULT_CANCELED: - case RESULT_FIRST_USER: - Toast.makeText(this, getString(R.string.version_update_error), Toast.LENGTH_LONG).show(); - break; - } - finish(); } } } -- cgit v1.2.3 From 13a495d18917f9b8952088b4a3e960239c5a168c Mon Sep 17 00:00:00 2001 From: cyBerta Date: Mon, 28 Dec 2020 18:44:00 +0100 Subject: cancel ongoing download notification if download failed --- .../java/se.leap.bitmaskclient/appUpdate/DownloadBroadcastReceiver.java | 1 + 1 file changed, 1 insertion(+) (limited to 'app/src/fatweb/java/se.leap.bitmaskclient') diff --git a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadBroadcastReceiver.java b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadBroadcastReceiver.java index 525c3c9b..a4acc2aa 100644 --- a/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadBroadcastReceiver.java +++ b/app/src/fatweb/java/se.leap.bitmaskclient/appUpdate/DownloadBroadcastReceiver.java @@ -81,6 +81,7 @@ public class DownloadBroadcastReceiver extends BroadcastReceiver { } else { Toast.makeText(context.getApplicationContext(), context.getString(R.string.version_update_error), Toast.LENGTH_LONG).show(); } + notificationManager.cancelNotifications(); break; case DOWNLOAD_PROGRESS: int progress = resultData.getInt(PROGRESS_VALUE, 0); -- cgit v1.2.3