Updater: add support for local updates
Allow importing and installation of OTA files already downloaded instead of requiring to reboot to recovery to install them. Squash of: - Add support for importing local updates Signed-off-by: Joey <jbevilacqua@shiftphones.com> Change-Id: I64ca3a6af29bdf8b2c6023a502f23080a27fd79e - OTA: read timestamp from imported zip metadata Signed-off-by: Joey <jbevilacqua@shiftphones.com> Change-Id: I93a5c0be81adab9ba8e50afde0e09839f059c9e0 - OTA: fix UI issues with local update Signed-off-by: Joey <jbevilacqua@shiftphones.com> Change-Id: I07c8f5507bc52c254c3dc1468fea495a073ae96c - OTA: fix local updates not being shown in UI (pt.2) Signed-off-by: Joey <jbevilacqua@shiftphones.com> Change-Id: Ife40eea05099eca9e1ee84c6f87d2715e5981cab - OTA: ignore download status changes for local updates Signed-off-by: Joey <jbevilacqua@shiftphones.com> Change-Id: I198f9b5462718f8a6e5687c891f3bfc6b1c645bd - UpdaterService: fix crash with local install Change-Id: I27b187cf4adec986d516e3017d1b3877691029b2 Signed-off-by: Alexander Martinz <amartinz@shiftphones.com> - Local updates: do not remove local update from ui after installation Change-Id: I869e090f26273006f933ad99c42b7c6a2e963797 Signed-off-by: Alexander Martinz <amartinz@shiftphones.com> - Local updates: modify display version Change-Id: I8a39e0936040bb9546499754ab4a9ef60c56aca0 Signed-off-by: Alexander Martinz <amartinz@shiftphones.com> - Local updates: show build date in import dialog Change-Id: I9014358ea1cf941e76fdd80a5147e9d924fc1a8f Signed-off-by: Alexander Martinz <amartinz@shiftphones.com> Change-Id: I64ca3a6af29bdf8b2c6023a502f23080a27fd79e Signed-off-by: Joey <joey@lineageos.org> Signed-off-by: Alexander Martinz <amartinz@shiftphones.com>
This commit is contained in:
249
app/src/main/java/org/lineageos/updater/UpdateImporter.java
Normal file
249
app/src/main/java/org/lineageos/updater/UpdateImporter.java
Normal file
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2022 The LineageOS Project
|
||||
* Copyright (C) 2020-2022 SHIFT GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.lineageos.updater;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.util.Log;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.lineageos.updater.controller.UpdaterController;
|
||||
import org.lineageos.updater.controller.UpdaterService;
|
||||
import org.lineageos.updater.misc.StringGenerator;
|
||||
import org.lineageos.updater.misc.Utils;
|
||||
import org.lineageos.updater.model.Update;
|
||||
import org.lineageos.updater.model.UpdateInfo;
|
||||
import org.lineageos.updater.model.UpdateStatus;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.DateFormat;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
public class UpdateImporter {
|
||||
private static final int REQUEST_PICK = 9061;
|
||||
private static final String TAG = "UpdateImporter";
|
||||
private static final String MIME_ZIP = "application/zip";
|
||||
private static final String FILE_NAME = "localUpdate.zip";
|
||||
private static final String METADATA_PATH = "META-INF/com/android/metadata";
|
||||
private static final String METADATA_TIMESTAMP_KEY = "post-timestamp=";
|
||||
|
||||
private final Activity activity;
|
||||
private final Callbacks callbacks;
|
||||
|
||||
private Thread workingThread;
|
||||
|
||||
public UpdateImporter(Activity activity, Callbacks callbacks) {
|
||||
this.activity = activity;
|
||||
this.callbacks = callbacks;
|
||||
}
|
||||
|
||||
public void stopImport() {
|
||||
if (workingThread != null && workingThread.isAlive()) {
|
||||
workingThread.interrupt();
|
||||
workingThread = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void openImportPicker() {
|
||||
final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT)
|
||||
.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
.setType(MIME_ZIP);
|
||||
activity.startActivityForResult(intent, REQUEST_PICK);
|
||||
}
|
||||
|
||||
public boolean onResult(int requestCode, int resultCode, Intent data) {
|
||||
if (resultCode != Activity.RESULT_OK || requestCode != REQUEST_PICK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return onPicked(data.getData());
|
||||
}
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
private boolean onPicked(Uri uri) {
|
||||
callbacks.onImportStarted();
|
||||
|
||||
workingThread = new Thread(() -> {
|
||||
File importedFile = null;
|
||||
try {
|
||||
importedFile = importFile(uri);
|
||||
verifyPackage(importedFile);
|
||||
|
||||
final Update update = buildLocalUpdate(importedFile);
|
||||
addUpdate(update);
|
||||
activity.runOnUiThread(() -> callbacks.onImportCompleted(update));
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to import update package", e);
|
||||
// Do not store invalid update
|
||||
if (importedFile != null) {
|
||||
importedFile.delete();
|
||||
}
|
||||
|
||||
activity.runOnUiThread(() -> callbacks.onImportCompleted(null));
|
||||
}
|
||||
});
|
||||
workingThread.start();
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressLint("SetWorldReadable")
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
private File importFile(Uri uri) throws IOException {
|
||||
final ParcelFileDescriptor parcelDescriptor = activity.getContentResolver()
|
||||
.openFileDescriptor(uri, "r");
|
||||
if (parcelDescriptor == null) {
|
||||
throw new IOException("Failed to obtain fileDescriptor");
|
||||
}
|
||||
|
||||
final FileInputStream iStream = new FileInputStream(parcelDescriptor
|
||||
.getFileDescriptor());
|
||||
final File downloadDir = Utils.getDownloadPath(activity);
|
||||
final File outFile = new File(downloadDir, FILE_NAME);
|
||||
if (outFile.exists()) {
|
||||
outFile.delete();
|
||||
}
|
||||
final FileOutputStream oStream = new FileOutputStream(outFile);
|
||||
|
||||
int read;
|
||||
final byte[] buffer = new byte[4096];
|
||||
while ((read = iStream.read(buffer)) > 0) {
|
||||
oStream.write(buffer, 0, read);
|
||||
}
|
||||
oStream.flush();
|
||||
oStream.close();
|
||||
iStream.close();
|
||||
parcelDescriptor.close();
|
||||
|
||||
outFile.setReadable(true, false);
|
||||
|
||||
return outFile;
|
||||
}
|
||||
|
||||
private Update buildLocalUpdate(File file) {
|
||||
final long timeStamp = getTimeStamp(file);
|
||||
final String buildDate = StringGenerator.getDateLocalizedUTC(
|
||||
activity, DateFormat.MEDIUM, timeStamp);
|
||||
final String name = activity.getString(R.string.local_update_name);
|
||||
final Update update = new Update();
|
||||
update.setAvailableOnline(false);
|
||||
update.setName(name);
|
||||
update.setFile(file);
|
||||
update.setFileSize(file.length());
|
||||
update.setDownloadId(Update.LOCAL_ID);
|
||||
update.setTimestamp(timeStamp);
|
||||
update.setStatus(UpdateStatus.VERIFIED);
|
||||
update.setPersistentStatus(UpdateStatus.Persistent.VERIFIED);
|
||||
update.setVersion(String.format("%s (%s)", name, buildDate));
|
||||
return update;
|
||||
}
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
private void verifyPackage(File file) throws Exception {
|
||||
try {
|
||||
android.os.RecoverySystem.verifyPackage(file, null, null);
|
||||
} catch (Exception e) {
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
throw new Exception("Verification failed, file has been deleted");
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addUpdate(Update update) {
|
||||
UpdaterController controller = UpdaterController.getInstance(activity);
|
||||
controller.addUpdate(update, false);
|
||||
}
|
||||
|
||||
private long getTimeStamp(File file) {
|
||||
try {
|
||||
final String metadataContent = readZippedFile(file, METADATA_PATH);
|
||||
final String[] lines = metadataContent.split("\n");
|
||||
for (String line : lines) {
|
||||
if (!line.startsWith(METADATA_TIMESTAMP_KEY)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final String timeStampStr = line.replace(METADATA_TIMESTAMP_KEY, "");
|
||||
return Long.parseLong(timeStampStr);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to read date from local update zip package", e);
|
||||
} catch (NumberFormatException e) {
|
||||
Log.e(TAG, "Failed to parse timestamp number from zip metadata file", e);
|
||||
}
|
||||
|
||||
Log.e(TAG, "Couldn't find timestamp in zip file, falling back to $now");
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private String readZippedFile(File file, String path) throws IOException {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
InputStream iStream = null;
|
||||
|
||||
try (final ZipFile zip = new ZipFile(file)) {
|
||||
final Enumeration<? extends ZipEntry> iterator = zip.entries();
|
||||
while (iterator.hasMoreElements()) {
|
||||
final ZipEntry entry = iterator.nextElement();
|
||||
if (!METADATA_PATH.equals(entry.getName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
iStream = zip.getInputStream(entry);
|
||||
break;
|
||||
}
|
||||
|
||||
if (iStream == null) {
|
||||
throw new FileNotFoundException("Couldn't find " + path + " in " + file.getName());
|
||||
}
|
||||
|
||||
final byte[] buffer = new byte[1024];
|
||||
int read;
|
||||
while ((read = iStream.read(buffer)) > 0) {
|
||||
sb.append(new String(buffer, 0, read, StandardCharsets.UTF_8));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to read file from zip package", e);
|
||||
throw e;
|
||||
} finally {
|
||||
if (iStream != null) {
|
||||
iStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public interface Callbacks {
|
||||
void onImportStarted();
|
||||
|
||||
void onImportCompleted(Update update);
|
||||
}
|
||||
}
|
@@ -17,6 +17,7 @@ package org.lineageos.updater;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.app.UiModeManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
@@ -49,6 +50,7 @@ import android.widget.Toast;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.SwitchCompat;
|
||||
@@ -72,6 +74,7 @@ import org.lineageos.updater.misc.BuildInfoUtils;
|
||||
import org.lineageos.updater.misc.Constants;
|
||||
import org.lineageos.updater.misc.StringGenerator;
|
||||
import org.lineageos.updater.misc.Utils;
|
||||
import org.lineageos.updater.model.Update;
|
||||
import org.lineageos.updater.model.UpdateInfo;
|
||||
|
||||
import java.io.File;
|
||||
@@ -80,7 +83,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class UpdatesActivity extends UpdatesListActivity {
|
||||
public class UpdatesActivity extends UpdatesListActivity implements UpdateImporter.Callbacks {
|
||||
|
||||
private static final String TAG = "UpdatesActivity";
|
||||
private UpdaterService mUpdaterService;
|
||||
@@ -106,11 +109,17 @@ public class UpdatesActivity extends UpdatesListActivity {
|
||||
}
|
||||
});
|
||||
|
||||
private UpdateImporter mUpdateImporter;
|
||||
@SuppressWarnings("deprecation")
|
||||
private ProgressDialog importDialog;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_updates);
|
||||
|
||||
mUpdateImporter = new UpdateImporter(this, this);
|
||||
|
||||
UiModeManager uiModeManager = getSystemService(UiModeManager.class);
|
||||
mIsTV = uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION;
|
||||
|
||||
@@ -234,6 +243,17 @@ public class UpdatesActivity extends UpdatesListActivity {
|
||||
LocalBroadcastManager.getInstance(this).registerReceiver(mBroadcastReceiver, intentFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
if (importDialog != null) {
|
||||
importDialog.dismiss();
|
||||
importDialog = null;
|
||||
mUpdateImporter.stopImport();
|
||||
}
|
||||
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
LocalBroadcastManager.getInstance(this).unregisterReceiver(mBroadcastReceiver);
|
||||
@@ -263,6 +283,9 @@ public class UpdatesActivity extends UpdatesListActivity {
|
||||
Uri.parse(Utils.getChangelogURL(this)));
|
||||
startActivity(openUrl);
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_local_update) {
|
||||
mUpdateImporter.openImportPicker();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
@@ -273,8 +296,60 @@ public class UpdatesActivity extends UpdatesListActivity {
|
||||
return true;
|
||||
}
|
||||
|
||||
private final ServiceConnection mConnection = new ServiceConnection() {
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
||||
if (!mUpdateImporter.onResult(requestCode, resultCode, data)) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public void onImportStarted() {
|
||||
if (importDialog != null && importDialog.isShowing()) {
|
||||
importDialog.dismiss();
|
||||
}
|
||||
|
||||
importDialog = ProgressDialog.show(this, getString(R.string.local_update_import),
|
||||
getString(R.string.local_update_import_progress), true, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImportCompleted(Update update) {
|
||||
if (importDialog != null) {
|
||||
importDialog.dismiss();
|
||||
importDialog = null;
|
||||
}
|
||||
|
||||
if (update == null) {
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.local_update_import)
|
||||
.setMessage(R.string.local_update_import_failure)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show();
|
||||
return;
|
||||
}
|
||||
|
||||
mAdapter.notifyDataSetChanged();
|
||||
|
||||
final Runnable deleteUpdate = () -> UpdaterController.getInstance(this)
|
||||
.deleteUpdate(update.getDownloadId());
|
||||
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.local_update_import)
|
||||
.setMessage(getString(R.string.local_update_import_success, update.getVersion()))
|
||||
.setPositiveButton(R.string.local_update_import_install, (dialog, which) -> {
|
||||
mAdapter.addItem(update.getDownloadId());
|
||||
// Update UI
|
||||
getUpdatesList();
|
||||
Utils.triggerUpdate(this, update.getDownloadId());
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, (dialog, which) -> deleteUpdate.run())
|
||||
.setOnCancelListener((dialog) -> deleteUpdate.run())
|
||||
.show();
|
||||
}
|
||||
|
||||
private final ServiceConnection mConnection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName className,
|
||||
IBinder service) {
|
||||
@@ -425,6 +500,10 @@ public class UpdatesActivity extends UpdatesListActivity {
|
||||
}
|
||||
|
||||
private void handleDownloadStatusChange(String downloadId) {
|
||||
if (Update.LOCAL_ID.equals(downloadId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateInfo update = mUpdaterService.getUpdaterController().getUpdate(downloadId);
|
||||
switch (update.getStatus()) {
|
||||
case PAUSED_ERROR:
|
||||
|
@@ -65,6 +65,7 @@ import org.lineageos.updater.model.UpdateStatus;
|
||||
import java.io.IOException;
|
||||
import java.text.DateFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class UpdatesListAdapter extends RecyclerView.Adapter<UpdatesListAdapter.ViewHolder> {
|
||||
@@ -297,6 +298,14 @@ public class UpdatesListAdapter extends RecyclerView.Adapter<UpdatesListAdapter.
|
||||
mDownloadIds = downloadIds;
|
||||
}
|
||||
|
||||
public void addItem(String downloadId) {
|
||||
if (mDownloadIds == null) {
|
||||
mDownloadIds = new ArrayList<>();
|
||||
}
|
||||
mDownloadIds.add(0, downloadId);
|
||||
notifyItemInserted(0);
|
||||
}
|
||||
|
||||
public void notifyItemChanged(String downloadId) {
|
||||
if (mDownloadIds == null) {
|
||||
return;
|
||||
|
@@ -159,6 +159,10 @@ class ABUpdateInstaller {
|
||||
mDownloadId = downloadId;
|
||||
|
||||
File file = mUpdaterController.getActualUpdate(mDownloadId).getFile();
|
||||
install(file, downloadId);
|
||||
}
|
||||
|
||||
public void install(File file, String downloadId) {
|
||||
if (!file.exists()) {
|
||||
Log.e(TAG, "The given update doesn't exist");
|
||||
mUpdaterController.getActualUpdate(downloadId)
|
||||
|
@@ -66,7 +66,7 @@ public class UpdaterController {
|
||||
private int mActiveDownloads = 0;
|
||||
private final Set<String> mVerifyingUpdates = new HashSet<>();
|
||||
|
||||
protected static synchronized UpdaterController getInstance(Context context) {
|
||||
public static synchronized UpdaterController getInstance(Context context) {
|
||||
if (sUpdaterController == null) {
|
||||
sUpdaterController = new UpdaterController(context);
|
||||
}
|
||||
@@ -330,7 +330,7 @@ public class UpdaterController {
|
||||
return addUpdate(update, true);
|
||||
}
|
||||
|
||||
private boolean addUpdate(final UpdateInfo updateInfo, boolean availableOnline) {
|
||||
public boolean addUpdate(final UpdateInfo updateInfo, boolean availableOnline) {
|
||||
Log.d(TAG, "Adding download: " + updateInfo.getDownloadId());
|
||||
if (mDownloads.containsKey(updateInfo.getDownloadId())) {
|
||||
Log.d(TAG, "Download (" + updateInfo.getDownloadId() + ") already added");
|
||||
@@ -470,7 +470,7 @@ public class UpdaterController {
|
||||
}
|
||||
|
||||
public void deleteUpdate(String downloadId) {
|
||||
Log.d(TAG, "Cancelling " + downloadId);
|
||||
Log.d(TAG, "Deleting update: " + downloadId);
|
||||
if (!mDownloads.containsKey(downloadId) || isDownloading(downloadId)) {
|
||||
return;
|
||||
}
|
||||
@@ -482,7 +482,8 @@ public class UpdaterController {
|
||||
update.setPersistentStatus(UpdateStatus.Persistent.UNKNOWN);
|
||||
deleteUpdateAsync(update);
|
||||
|
||||
if (!update.getAvailableOnline()) {
|
||||
final boolean isLocalUpdate = Update.LOCAL_ID.equals(downloadId);
|
||||
if (!isLocalUpdate && !update.getAvailableOnline()) {
|
||||
Log.d(TAG, "Download no longer available online, removing");
|
||||
mDownloads.remove(downloadId);
|
||||
notifyUpdateDelete(downloadId);
|
||||
|
@@ -41,9 +41,11 @@ import org.lineageos.updater.misc.BuildInfoUtils;
|
||||
import org.lineageos.updater.misc.Constants;
|
||||
import org.lineageos.updater.misc.StringGenerator;
|
||||
import org.lineageos.updater.misc.Utils;
|
||||
import org.lineageos.updater.model.Update;
|
||||
import org.lineageos.updater.model.UpdateInfo;
|
||||
import org.lineageos.updater.model.UpdateStatus;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.text.DateFormat;
|
||||
import java.text.NumberFormat;
|
||||
@@ -122,8 +124,10 @@ public class UpdaterService extends Service {
|
||||
setNotificationTitle(update);
|
||||
handleInstallProgress(update);
|
||||
} else if (UpdaterController.ACTION_UPDATE_REMOVED.equals(intent.getAction())) {
|
||||
final boolean isLocalUpdate = Update.LOCAL_ID.equals(downloadId);
|
||||
Bundle extras = mNotificationBuilder.getExtras();
|
||||
if (downloadId.equals(extras.getString(UpdaterController.EXTRA_DOWNLOAD_ID))) {
|
||||
if (extras != null && !isLocalUpdate && downloadId.equals(
|
||||
extras.getString(UpdaterController.EXTRA_DOWNLOAD_ID))) {
|
||||
mNotificationBuilder.setExtras(null);
|
||||
UpdateInfo update = mUpdaterController.getUpdate(downloadId);
|
||||
if (update.getStatus() != UpdateStatus.INSTALLED) {
|
||||
@@ -408,7 +412,9 @@ public class UpdaterService extends Service {
|
||||
|
||||
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
boolean deleteUpdate = pref.getBoolean(Constants.PREF_AUTO_DELETE_UPDATES, false);
|
||||
if (deleteUpdate) {
|
||||
boolean isLocal = Update.LOCAL_ID.equals(update.getDownloadId());
|
||||
// Always delete local updates
|
||||
if (deleteUpdate || isLocal) {
|
||||
mUpdaterController.deleteUpdate(update.getDownloadId());
|
||||
}
|
||||
|
||||
|
@@ -18,6 +18,7 @@ package org.lineageos.updater.model;
|
||||
import java.io.File;
|
||||
|
||||
public class Update extends UpdateBase implements UpdateInfo {
|
||||
public static final String LOCAL_ID = "local";
|
||||
|
||||
private UpdateStatus mStatus = UpdateStatus.UNKNOWN;
|
||||
private int mPersistentStatus = UpdateStatus.Persistent.UNKNOWN;
|
||||
|
@@ -6,6 +6,10 @@
|
||||
android:icon="@drawable/ic_menu_refresh"
|
||||
android:title="@string/menu_refresh"
|
||||
app:showAsAction="ifRoom" />
|
||||
<item
|
||||
android:id="@+id/menu_local_update"
|
||||
android:title="@string/local_update_import"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/menu_preferences"
|
||||
android:title="@string/menu_preferences"
|
||||
|
@@ -157,4 +157,11 @@
|
||||
<string name="info_dialog_title">Did you know?</string>
|
||||
<string name="info_dialog_message">LineageOS updates are full installation packages. That means you can always install only the latest update, even if you skipped some in between!</string>
|
||||
<string name="info_dialog_ok">Thanks for the info!</string>
|
||||
|
||||
<string name="local_update_import">Local update</string>
|
||||
<string name="local_update_import_progress">Importing local update\u2026</string>
|
||||
<string name="local_update_import_success">%1$s has been imported. Do you want to install it?</string>
|
||||
<string name="local_update_import_failure">Failed to import local update</string>
|
||||
<string name="local_update_import_install">Install</string>
|
||||
<string name="local_update_name">Local update</string>
|
||||
</resources>
|
||||
|
Reference in New Issue
Block a user