├── .gitignore ├── README.md ├── app ├── .gitignore ├── Android.bp ├── build.gradle.kts ├── default-permissions_org.lineageos.updater.xml ├── privapp_whitelist_org.lineageos.updater.xml ├── proguard.flags └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── org │ │ └── lineageos │ │ └── updater │ │ ├── ExportUpdateService.java │ │ ├── UpdateImporter.java │ │ ├── UpdaterReceiver.java │ │ ├── UpdatesActivity.java │ │ ├── UpdatesCheckReceiver.java │ │ ├── UpdatesDbHelper.java │ │ ├── UpdatesListActivity.java │ │ ├── UpdatesListAdapter.java │ │ ├── controller │ │ ├── ABUpdateInstaller.java │ │ ├── UpdateInstaller.java │ │ ├── UpdaterController.java │ │ └── UpdaterService.java │ │ ├── download │ │ ├── DownloadClient.java │ │ └── HttpURLConnectionClient.java │ │ ├── misc │ │ ├── BuildInfoUtils.java │ │ ├── Constants.java │ │ ├── FileUtils.java │ │ ├── StringGenerator.java │ │ └── Utils.java │ │ └── model │ │ ├── Update.java │ │ ├── UpdateBase.java │ │ ├── UpdateBaseInfo.java │ │ ├── UpdateInfo.java │ │ └── UpdateStatus.java │ └── res │ ├── drawable │ ├── ic_launcher_background.xml │ ├── ic_launcher_foreground.xml │ ├── ic_menu_preferences.xml │ ├── ic_menu_refresh.xml │ ├── ic_pause.xml │ └── ic_system_update.xml │ ├── layout-television │ ├── activity_updates.xml │ └── update_item_view.xml │ ├── layout │ ├── activity_updates.xml │ ├── checkbox_view.xml │ ├── preferences_dialog.xml │ ├── progress_dialog.xml │ └── update_item_view.xml │ ├── menu │ ├── menu_action_mode.xml │ └── menu_toolbar.xml │ ├── mipmap-anydpi │ └── ic_launcher.xml │ ├── values-ar │ └── strings.xml │ ├── values-as │ └── strings.xml │ ├── values-ast-rES │ └── strings.xml │ ├── values-az │ └── strings.xml │ ├── values-be │ └── strings.xml │ ├── values-bg │ └── strings.xml │ ├── values-bn │ └── strings.xml │ ├── values-bs │ └── strings.xml │ ├── values-ca │ └── strings.xml │ ├── values-cs │ └── strings.xml │ ├── values-cy │ └── strings.xml │ ├── values-da │ └── strings.xml │ ├── values-de │ └── strings.xml │ ├── values-el │ └── strings.xml │ ├── values-en-rAU │ └── strings.xml │ ├── values-en-rCA │ └── strings.xml │ ├── values-en-rGB │ └── strings.xml │ ├── values-en-rIN │ └── strings.xml │ ├── values-es-rMX │ └── strings.xml │ ├── values-es-rUS │ └── strings.xml │ ├── values-es │ └── strings.xml │ ├── values-et │ └── strings.xml │ ├── values-eu │ └── strings.xml │ ├── values-fa │ └── strings.xml │ ├── values-fi │ └── strings.xml │ ├── values-fr │ └── strings.xml │ ├── values-fur-rIT │ └── strings.xml │ ├── values-fy-rNL │ └── strings.xml │ ├── values-ga-rIE │ └── strings.xml │ ├── values-gd │ └── strings.xml │ ├── values-gl │ └── strings.xml │ ├── values-hr │ └── strings.xml │ ├── values-hu │ └── strings.xml │ ├── values-in │ └── strings.xml │ ├── values-is │ └── strings.xml │ ├── values-it │ └── strings.xml │ ├── values-iw │ └── strings.xml │ ├── values-ja │ └── strings.xml │ ├── values-ka │ └── strings.xml │ ├── values-kab-rDZ │ └── strings.xml │ ├── values-kn │ └── strings.xml │ ├── values-ko │ └── strings.xml │ ├── values-nb │ └── strings.xml │ ├── values-night │ ├── bools.xml │ └── colors.xml │ ├── values-nl │ └── strings.xml │ ├── values-pl │ └── strings.xml │ ├── values-pt-rBR │ └── strings.xml │ ├── values-pt-rPT │ └── strings.xml │ ├── values-ro │ └── strings.xml │ ├── values-ru │ └── strings.xml │ ├── values-sc-rIT │ └── strings.xml │ ├── values-sk │ └── strings.xml │ ├── values-sl │ └── strings.xml │ ├── values-sq │ └── strings.xml │ ├── values-sr │ └── strings.xml │ ├── values-sv │ └── strings.xml │ ├── values-ta │ └── strings.xml │ ├── values-television │ └── styles.xml │ ├── values-th │ └── strings.xml │ ├── values-tr │ └── strings.xml │ ├── values-uk │ └── strings.xml │ ├── values-vi │ └── strings.xml │ ├── values-zh-rCN │ └── strings.xml │ ├── values-zh-rTW │ └── strings.xml │ └── values │ ├── arrays.xml │ ├── bools.xml │ ├── colors.xml │ ├── config.xml │ ├── integers.xml │ ├── strings.xml │ ├── styles.xml │ └── symbols.xml ├── build.gradle.kts ├── gen-keystore.sh ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── keystore.properties.sample ├── push-update.sh └── settings.gradle.kts /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | /system_libs/*.jar 9 | /keystore.properties 10 | .externalNativeBuild 11 | .cxx 12 | *.jks 13 | local.properties 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Updater 2 | ======= 3 | Simple application to download and apply OTA packages. 4 | 5 | 6 | Server requirements 7 | ------------------- 8 | The app sends `GET` requests to the URL defined by the `updater_server_url` 9 | resource (or the `lineage.updater.uri` system property) and expects as response 10 | a JSON with the following structure: 11 | ```json 12 | { 13 | "response": [ 14 | { 15 | "datetime": 1230764400, 16 | "filename": "ota-package.zip", 17 | "id": "5eb63bbbe01eeed093cb22bb8f5acdc3", 18 | "romtype": "nightly", 19 | "size": 314572800, 20 | "url": "https://example.com/ota-package.zip", 21 | "version": "15.1" 22 | } 23 | ] 24 | } 25 | ``` 26 | 27 | The `datetime` attribute is the build date expressed as UNIX timestamp. 28 | The `filename` attribute is the name of the file to be downloaded. 29 | The `id` attribute is a string that uniquely identifies the update. 30 | The `romtype` attribute is the string to be compared with the `ro.lineage.releasetype` property. 31 | The `size` attribute is the size of the update expressed in bytes. 32 | The `url` attribute is the URL of the file to be downloaded. 33 | The `version` attribute is the string to be compared with the `ro.lineage.build.version` property. 34 | 35 | Additional attributes are ignored. 36 | 37 | 38 | Build with Android Studio 39 | ------------------------- 40 | Updater needs access to the system API, therefore it can't be built only using 41 | the public SDK. You first need to generate the libraries with all the needed 42 | classes. The application also needs elevated privileges, so you need to sign 43 | it with the right key to update the one in the system partition. To do this: 44 | 45 | - Place this directory anywhere in the Android source tree 46 | - Generate a keystore and keystore.properties using `gen-keystore.sh` 47 | - Build the dependencies running `make UpdaterStudio` from the root of the 48 | Android source tree. This command will add the needed libraries in 49 | `system_libraries/`. 50 | 51 | You need to do the above once, unless Android Studio can't find some symbol. 52 | In this case, rebuild the system libraries with `make UpdaterStudio`. 53 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/Android.bp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2022-2024 The LineageOS Project 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | android_app { 8 | name: "Updater", 9 | 10 | // Include SettingsLib and its dependencies 11 | defaults: ["SettingsLibDefaults"], 12 | 13 | srcs: ["src/main/java/**/*.java"], 14 | resource_dirs: ["src/main/res"], 15 | manifest: "src/main/AndroidManifest.xml", 16 | 17 | platform_apis: true, 18 | privileged: true, 19 | certificate: "platform", 20 | system_ext_specific: true, 21 | 22 | overrides: [ 23 | "SystemUpdater", 24 | ], 25 | 26 | static_libs: [ 27 | // DO NOT EDIT THIS SECTION MANUALLY 28 | "androidx.core_core-ktx", 29 | "androidx.appcompat_appcompat", 30 | "androidx.cardview_cardview", 31 | "androidx.lifecycle_lifecycle-viewmodel-ktx", 32 | "androidx.localbroadcastmanager_localbroadcastmanager", 33 | "androidx.preference_preference", 34 | "androidx.recyclerview_recyclerview", 35 | "com.google.android.material_material", 36 | ], 37 | 38 | optimize: { 39 | proguard_flags_files: ["proguard.flags"], 40 | }, 41 | 42 | required: [ 43 | "privapp_whitelist_org.lineageos.updater", 44 | "default-permissions_org.lineageos.updater" 45 | ], 46 | } 47 | 48 | prebuilt_etc { 49 | name: "privapp_whitelist_org.lineageos.updater", 50 | system_ext_specific: true, 51 | sub_dir: "permissions", 52 | src: "privapp_whitelist_org.lineageos.updater.xml", 53 | filename_from_src: true, 54 | } 55 | 56 | prebuilt_etc { 57 | name: "default-permissions_org.lineageos.updater", 58 | system_ext_specific: true, 59 | sub_dir: "default-permissions", 60 | src: "default-permissions_org.lineageos.updater.xml", 61 | filename_from_src: true, 62 | } 63 | -------------------------------------------------------------------------------- /app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import java.util.Properties 2 | import org.lineageos.generatebp.GenerateBpPlugin 3 | import org.lineageos.generatebp.GenerateBpPluginExtension 4 | import org.lineageos.generatebp.models.Module 5 | 6 | plugins { 7 | id("com.android.application") 8 | id("kotlin-android") 9 | } 10 | 11 | apply { 12 | plugin() 13 | } 14 | 15 | buildscript { 16 | repositories { 17 | maven("https://raw.githubusercontent.com/lineage-next/gradle-generatebp/v1.2/.m2") 18 | } 19 | 20 | dependencies { 21 | classpath("org.lineageos:gradle-generatebp:+") 22 | } 23 | } 24 | 25 | val keystorePropertiesFile = rootProject.file("keystore.properties") 26 | val keystoreProperties = Properties().apply { 27 | if (keystorePropertiesFile.exists()) { 28 | load(keystorePropertiesFile.inputStream()) 29 | } 30 | } 31 | 32 | android { 33 | compileSdk = 33 34 | 35 | defaultConfig { 36 | applicationId = "org.lineageos.updater" 37 | minSdk = 32 38 | targetSdk = 33 39 | versionCode = 1 40 | versionName = "1.0" 41 | } 42 | 43 | buildTypes { 44 | getByName("release") { 45 | // Includes the default ProGuard rules files. 46 | setProguardFiles( 47 | listOf( 48 | getDefaultProguardFile("proguard-android-optimize.txt"), 49 | "proguard-rules.pro" 50 | ) 51 | ) 52 | } 53 | getByName("debug") { 54 | // Append .dev to package name so we won't conflict with AOSP build. 55 | applicationIdSuffix = ".dev" 56 | } 57 | } 58 | 59 | compileOptions { 60 | sourceCompatibility = JavaVersion.VERSION_11 61 | targetCompatibility = JavaVersion.VERSION_11 62 | } 63 | 64 | kotlinOptions { 65 | jvmTarget = "11" 66 | } 67 | 68 | signingConfigs { 69 | create("release") { 70 | (keystoreProperties["keyAlias"] as String?)?.let { 71 | keyAlias = it 72 | } 73 | (keystoreProperties["keyPassword"] as String?)?.let { 74 | keyPassword = it 75 | } 76 | (keystoreProperties["storeFile"] as String?)?.let { 77 | storeFile = file(it) 78 | } 79 | (keystoreProperties["storePassword"] as String?)?.let { 80 | storePassword = it 81 | } 82 | } 83 | } 84 | namespace = "org.lineageos.updater" 85 | } 86 | 87 | dependencies { 88 | compileOnly(fileTree(mapOf("dir" to "../system_libs", "include" to listOf("*.jar")))) 89 | 90 | implementation("androidx.core:core-ktx:1.9.0") 91 | implementation("androidx.appcompat:appcompat:1.6.1") 92 | implementation("androidx.cardview:cardview:1.0.0") 93 | implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1") 94 | implementation("androidx.localbroadcastmanager:localbroadcastmanager:1.1.0") 95 | implementation("androidx.preference:preference:1.2.0") 96 | implementation("androidx.recyclerview:recyclerview:1.2.1") 97 | implementation("com.google.android.material:material:1.9.0-alpha01") 98 | } 99 | 100 | configure { 101 | targetSdk.set(android.defaultConfig.targetSdk!!) 102 | availableInAOSP.set { module: Module -> 103 | when { 104 | module.group.startsWith("androidx") -> true 105 | module.group.startsWith("org.jetbrains") -> true 106 | module.group == "com.google.android.material" -> true 107 | module.group == "com.google.errorprone" -> true 108 | module.group == "com.google.guava" -> true 109 | module.group == "junit" -> true 110 | else -> false 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /app/default-permissions_org.lineageos.updater.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/privapp_whitelist_org.lineageos.updater.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/proguard.flags: -------------------------------------------------------------------------------- 1 | -keep class org.lineageos.updater.ui.FlingBehavior { *; } 2 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 25 | 26 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 50 | 52 | 53 | 56 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /app/src/main/java/org/lineageos/updater/UpdaterReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2022 The LineageOS Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.lineageos.updater; 17 | 18 | import android.app.NotificationChannel; 19 | import android.app.NotificationManager; 20 | import android.app.PendingIntent; 21 | import android.content.BroadcastReceiver; 22 | import android.content.Context; 23 | import android.content.Intent; 24 | import android.content.SharedPreferences; 25 | import android.os.PowerManager; 26 | import android.os.SystemProperties; 27 | 28 | import androidx.core.app.NotificationCompat; 29 | import androidx.preference.PreferenceManager; 30 | 31 | import org.lineageos.updater.misc.BuildInfoUtils; 32 | import org.lineageos.updater.misc.Constants; 33 | import org.lineageos.updater.misc.StringGenerator; 34 | 35 | import java.text.DateFormat; 36 | 37 | public class UpdaterReceiver extends BroadcastReceiver { 38 | 39 | public static final String ACTION_INSTALL_REBOOT = 40 | "org.lineageos.updater.action.INSTALL_REBOOT"; 41 | 42 | private static final String INSTALL_ERROR_NOTIFICATION_CHANNEL = 43 | "install_error_notification_channel"; 44 | 45 | private static boolean shouldShowUpdateFailedNotification(Context context) { 46 | SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); 47 | 48 | // We can't easily detect failed re-installations 49 | if (preferences.getBoolean(Constants.PREF_INSTALL_AGAIN, false) || 50 | preferences.getBoolean(Constants.PREF_INSTALL_NOTIFIED, false)) { 51 | return false; 52 | } 53 | 54 | long buildTimestamp = SystemProperties.getLong(Constants.PROP_BUILD_DATE, 0); 55 | long lastBuildTimestamp = preferences.getLong(Constants.PREF_INSTALL_OLD_TIMESTAMP, -1); 56 | return buildTimestamp == lastBuildTimestamp; 57 | } 58 | 59 | private static void showUpdateFailedNotification(Context context) { 60 | SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); 61 | String buildDate = StringGenerator.getDateLocalizedUTC(context, 62 | DateFormat.MEDIUM, preferences.getLong(Constants.PREF_INSTALL_NEW_TIMESTAMP, 0)); 63 | String buildInfo = context.getString(R.string.list_build_version_date, 64 | BuildInfoUtils.getBuildVersion(), buildDate); 65 | 66 | Intent notificationIntent = new Intent(context, UpdatesActivity.class); 67 | PendingIntent intent = PendingIntent.getActivity(context, 0, notificationIntent, 68 | PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); 69 | 70 | NotificationChannel notificationChannel = new NotificationChannel( 71 | INSTALL_ERROR_NOTIFICATION_CHANNEL, 72 | context.getString(R.string.update_failed_channel_title), 73 | NotificationManager.IMPORTANCE_LOW); 74 | NotificationCompat.Builder builder = new NotificationCompat.Builder(context, 75 | INSTALL_ERROR_NOTIFICATION_CHANNEL) 76 | .setContentIntent(intent) 77 | .setSmallIcon(R.drawable.ic_system_update) 78 | .setContentTitle(context.getString(R.string.update_failed_notification)) 79 | .setStyle(new NotificationCompat.BigTextStyle().bigText(buildInfo)) 80 | .setContentText(buildInfo); 81 | 82 | NotificationManager nm = context.getSystemService(NotificationManager.class); 83 | nm.createNotificationChannel(notificationChannel); 84 | nm.notify(0, builder.build()); 85 | } 86 | 87 | @Override 88 | public void onReceive(Context context, Intent intent) { 89 | if (ACTION_INSTALL_REBOOT.equals(intent.getAction())) { 90 | PowerManager pm = context.getSystemService(PowerManager.class); 91 | pm.reboot(null); 92 | } else if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { 93 | SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context); 94 | pref.edit().remove(Constants.PREF_NEEDS_REBOOT_ID).apply(); 95 | 96 | if (shouldShowUpdateFailedNotification(context)) { 97 | pref.edit().putBoolean(Constants.PREF_INSTALL_NOTIFIED, true).apply(); 98 | showUpdateFailedNotification(context); 99 | } 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /app/src/main/java/org/lineageos/updater/UpdatesDbHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2022 The LineageOS Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.lineageos.updater; 17 | 18 | import android.content.ContentValues; 19 | import android.content.Context; 20 | import android.database.Cursor; 21 | import android.database.sqlite.SQLiteDatabase; 22 | import android.database.sqlite.SQLiteOpenHelper; 23 | import android.provider.BaseColumns; 24 | 25 | import org.lineageos.updater.model.Update; 26 | 27 | import java.io.File; 28 | import java.util.ArrayList; 29 | import java.util.List; 30 | 31 | public class UpdatesDbHelper extends SQLiteOpenHelper { 32 | 33 | public static final int DATABASE_VERSION = 1; 34 | public static final String DATABASE_NAME = "updates.db"; 35 | 36 | public static class UpdateEntry implements BaseColumns { 37 | public static final String TABLE_NAME = "updates"; 38 | public static final String COLUMN_NAME_STATUS = "status"; 39 | public static final String COLUMN_NAME_PATH = "path"; 40 | public static final String COLUMN_NAME_DOWNLOAD_ID = "download_id"; 41 | public static final String COLUMN_NAME_TIMESTAMP = "timestamp"; 42 | public static final String COLUMN_NAME_TYPE = "type"; 43 | public static final String COLUMN_NAME_VERSION = "version"; 44 | public static final String COLUMN_NAME_SIZE = "size"; 45 | } 46 | 47 | private static final String SQL_CREATE_ENTRIES = 48 | "CREATE TABLE " + UpdateEntry.TABLE_NAME + " (" + 49 | UpdateEntry._ID + " INTEGER PRIMARY KEY," + 50 | UpdateEntry.COLUMN_NAME_STATUS + " INTEGER," + 51 | UpdateEntry.COLUMN_NAME_PATH + " TEXT," + 52 | UpdateEntry.COLUMN_NAME_DOWNLOAD_ID + " TEXT NOT NULL UNIQUE," + 53 | UpdateEntry.COLUMN_NAME_TIMESTAMP + " INTEGER," + 54 | UpdateEntry.COLUMN_NAME_TYPE + " TEXT," + 55 | UpdateEntry.COLUMN_NAME_VERSION + " TEXT," + 56 | UpdateEntry.COLUMN_NAME_SIZE + " INTEGER)"; 57 | 58 | private static final String SQL_DELETE_ENTRIES = 59 | "DROP TABLE IF EXISTS " + UpdateEntry.TABLE_NAME; 60 | 61 | public UpdatesDbHelper(Context context) { 62 | super(context, DATABASE_NAME, null, DATABASE_VERSION); 63 | } 64 | 65 | public void onCreate(SQLiteDatabase db) { 66 | db.execSQL(SQL_CREATE_ENTRIES); 67 | } 68 | 69 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 70 | db.execSQL(SQL_DELETE_ENTRIES); 71 | onCreate(db); 72 | } 73 | 74 | public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { 75 | onUpgrade(db, oldVersion, newVersion); 76 | } 77 | 78 | public void addUpdateWithOnConflict(Update update, int conflictAlgorithm) { 79 | SQLiteDatabase db = getWritableDatabase(); 80 | ContentValues values = new ContentValues(); 81 | fillContentValues(update, values); 82 | db.insertWithOnConflict(UpdateEntry.TABLE_NAME, null, values, conflictAlgorithm); 83 | } 84 | 85 | private static void fillContentValues(Update update, ContentValues values) { 86 | values.put(UpdateEntry.COLUMN_NAME_STATUS, update.getPersistentStatus()); 87 | values.put(UpdateEntry.COLUMN_NAME_PATH, update.getFile().getAbsolutePath()); 88 | values.put(UpdateEntry.COLUMN_NAME_DOWNLOAD_ID, update.getDownloadId()); 89 | values.put(UpdateEntry.COLUMN_NAME_TIMESTAMP, update.getTimestamp()); 90 | values.put(UpdateEntry.COLUMN_NAME_TYPE, update.getType()); 91 | values.put(UpdateEntry.COLUMN_NAME_VERSION, update.getVersion()); 92 | values.put(UpdateEntry.COLUMN_NAME_SIZE, update.getFileSize()); 93 | } 94 | 95 | public void removeUpdate(String downloadId) { 96 | SQLiteDatabase db = getWritableDatabase(); 97 | String selection = UpdateEntry.COLUMN_NAME_DOWNLOAD_ID + " = ?"; 98 | String[] selectionArgs = {downloadId}; 99 | db.delete(UpdateEntry.TABLE_NAME, selection, selectionArgs); 100 | } 101 | 102 | public void changeUpdateStatus(Update update) { 103 | String selection = UpdateEntry.COLUMN_NAME_DOWNLOAD_ID + " = ?"; 104 | String[] selectionArgs = {update.getDownloadId()}; 105 | changeUpdateStatus(selection, selectionArgs, update.getPersistentStatus()); 106 | } 107 | 108 | private void changeUpdateStatus(String selection, String[] selectionArgs, 109 | int status) { 110 | SQLiteDatabase db = getWritableDatabase(); 111 | ContentValues values = new ContentValues(); 112 | values.put(UpdateEntry.COLUMN_NAME_STATUS, status); 113 | db.update(UpdateEntry.TABLE_NAME, values, selection, selectionArgs); 114 | } 115 | 116 | public List getUpdates() { 117 | return getUpdates(null, null); 118 | } 119 | 120 | public List getUpdates(String selection, String[] selectionArgs) { 121 | SQLiteDatabase db = getReadableDatabase(); 122 | String[] projection = { 123 | UpdateEntry.COLUMN_NAME_PATH, 124 | UpdateEntry.COLUMN_NAME_DOWNLOAD_ID, 125 | UpdateEntry.COLUMN_NAME_TIMESTAMP, 126 | UpdateEntry.COLUMN_NAME_TYPE, 127 | UpdateEntry.COLUMN_NAME_VERSION, 128 | UpdateEntry.COLUMN_NAME_STATUS, 129 | UpdateEntry.COLUMN_NAME_SIZE, 130 | }; 131 | String sort = UpdateEntry.COLUMN_NAME_TIMESTAMP + " DESC"; 132 | Cursor cursor = db.query(UpdateEntry.TABLE_NAME, projection, selection, selectionArgs, 133 | null, null, sort); 134 | List updates = new ArrayList<>(); 135 | if (cursor != null) { 136 | while (cursor.moveToNext()) { 137 | Update update = new Update(); 138 | int index = cursor.getColumnIndex(UpdateEntry.COLUMN_NAME_PATH); 139 | update.setFile(new File(cursor.getString(index))); 140 | update.setName(update.getFile().getName()); 141 | index = cursor.getColumnIndex(UpdateEntry.COLUMN_NAME_DOWNLOAD_ID); 142 | update.setDownloadId(cursor.getString(index)); 143 | index = cursor.getColumnIndex(UpdateEntry.COLUMN_NAME_TIMESTAMP); 144 | update.setTimestamp(cursor.getLong(index)); 145 | index = cursor.getColumnIndex(UpdateEntry.COLUMN_NAME_TYPE); 146 | update.setType(cursor.getString(index)); 147 | index = cursor.getColumnIndex(UpdateEntry.COLUMN_NAME_VERSION); 148 | update.setVersion(cursor.getString(index)); 149 | index = cursor.getColumnIndex(UpdateEntry.COLUMN_NAME_STATUS); 150 | update.setPersistentStatus(cursor.getInt(index)); 151 | index = cursor.getColumnIndex(UpdateEntry.COLUMN_NAME_SIZE); 152 | update.setFileSize(cursor.getLong(index)); 153 | updates.add(update); 154 | } 155 | cursor.close(); 156 | } 157 | return updates; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /app/src/main/java/org/lineageos/updater/UpdatesListActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The LineageOS Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.lineageos.updater; 17 | 18 | import androidx.appcompat.app.AppCompatActivity; 19 | 20 | import org.lineageos.updater.model.UpdateInfo; 21 | 22 | public abstract class UpdatesListActivity extends AppCompatActivity { 23 | public abstract void exportUpdate(UpdateInfo update); 24 | public abstract void showSnackbar(int stringId, int duration); 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/org/lineageos/updater/download/DownloadClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2022 The LineageOS Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.lineageos.updater.download; 17 | 18 | import java.io.File; 19 | import java.io.IOException; 20 | 21 | public interface DownloadClient { 22 | 23 | interface DownloadCallback { 24 | void onResponse(Headers headers); 25 | 26 | void onSuccess(); 27 | 28 | void onFailure(boolean cancelled); 29 | } 30 | 31 | interface ProgressListener { 32 | void update(long bytesRead, long contentLength, long speed, long eta); 33 | } 34 | 35 | interface Headers { 36 | String get(String name); 37 | } 38 | 39 | /** 40 | * Start the download. This method has no effect if the download already started. 41 | */ 42 | void start(); 43 | 44 | /** 45 | * Resume the download. The download will fail if the server can't fulfil the 46 | * partial content request and DownloadCallback.onFailure() will be called. 47 | * This method has no effect if the download already started or the destination 48 | * file doesn't exist. 49 | */ 50 | void resume(); 51 | 52 | /** 53 | * Cancel the download. This method has no effect if the download isn't ongoing. 54 | */ 55 | void cancel(); 56 | 57 | final class Builder { 58 | private String mUrl; 59 | private File mDestination; 60 | private DownloadClient.DownloadCallback mCallback; 61 | private DownloadClient.ProgressListener mProgressListener; 62 | private boolean mUseDuplicateLinks; 63 | 64 | public DownloadClient build() throws IOException { 65 | if (mUrl == null) { 66 | throw new IllegalStateException("No download URL defined"); 67 | } else if (mDestination == null) { 68 | throw new IllegalStateException("No download destination defined"); 69 | } else if (mCallback == null) { 70 | throw new IllegalStateException("No download callback defined"); 71 | } 72 | return new HttpURLConnectionClient(mUrl, mDestination, mProgressListener, mCallback, 73 | mUseDuplicateLinks); 74 | } 75 | 76 | public Builder setUrl(String url) { 77 | mUrl = url; 78 | return this; 79 | } 80 | 81 | public Builder setDestination(File destination) { 82 | mDestination = destination; 83 | return this; 84 | } 85 | 86 | public Builder setDownloadCallback(DownloadClient.DownloadCallback downloadCallback) { 87 | mCallback = downloadCallback; 88 | return this; 89 | } 90 | 91 | public Builder setProgressListener(DownloadClient.ProgressListener progressListener) { 92 | mProgressListener = progressListener; 93 | return this; 94 | } 95 | 96 | public Builder setUseDuplicateLinks(boolean useDuplicateLinks) { 97 | mUseDuplicateLinks = useDuplicateLinks; 98 | return this; 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /app/src/main/java/org/lineageos/updater/misc/BuildInfoUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The LineageOS Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.lineageos.updater.misc; 17 | 18 | import android.os.SystemProperties; 19 | 20 | public final class BuildInfoUtils { 21 | 22 | private BuildInfoUtils() { 23 | } 24 | 25 | public static long getBuildDateTimestamp() { 26 | return SystemProperties.getLong(Constants.PROP_BUILD_DATE, 0); 27 | } 28 | 29 | public static String getBuildVersion() { 30 | return SystemProperties.get(Constants.PROP_BUILD_VERSION); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/org/lineageos/updater/misc/Constants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2023 The LineageOS Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.lineageos.updater.misc; 17 | 18 | public final class Constants { 19 | 20 | private Constants() { 21 | } 22 | 23 | public static final String AB_PAYLOAD_BIN_PATH = "payload.bin"; 24 | public static final String AB_PAYLOAD_PROPERTIES_PATH = "payload_properties.txt"; 25 | 26 | public static final int AUTO_UPDATES_CHECK_INTERVAL_NEVER = 0; 27 | public static final int AUTO_UPDATES_CHECK_INTERVAL_DAILY = 1; 28 | public static final int AUTO_UPDATES_CHECK_INTERVAL_WEEKLY = 2; 29 | public static final int AUTO_UPDATES_CHECK_INTERVAL_MONTHLY = 3; 30 | 31 | public static final String PREF_LAST_UPDATE_CHECK = "last_update_check"; 32 | public static final String PREF_AUTO_UPDATES_CHECK_INTERVAL = "auto_updates_check_interval"; 33 | public static final String PREF_AUTO_DELETE_UPDATES = "auto_delete_updates"; 34 | public static final String PREF_AB_PERF_MODE = "ab_perf_mode"; 35 | public static final String PREF_METERED_NETWORK_WARNING = "pref_metered_network_warning"; 36 | public static final String PREF_MOBILE_DATA_WARNING = "pref_mobile_data_warning"; 37 | public static final String PREF_NEEDS_REBOOT_ID = "needs_reboot_id"; 38 | 39 | public static final String UNCRYPT_FILE_EXT = ".uncrypt"; 40 | 41 | public static final String PROP_AB_DEVICE = "ro.build.ab_update"; 42 | public static final String PROP_ALLOW_MAJOR_UPGRADES = "lineage.updater.allow_major_upgrades"; 43 | public static final String PROP_BUILD_DATE = "ro.build.date.utc"; 44 | public static final String PROP_BUILD_VERSION = "ro.lineage.build.version"; 45 | public static final String PROP_BUILD_VERSION_INCREMENTAL = "ro.build.version.incremental"; 46 | public static final String PROP_DEVICE = "ro.lineage.device"; 47 | public static final String PROP_NEXT_DEVICE = "ro.updater.next_device"; 48 | public static final String PROP_RELEASE_TYPE = "ro.lineage.releasetype"; 49 | public static final String PROP_UPDATER_ALLOW_DOWNGRADING = "lineage.updater.allow_downgrading"; 50 | public static final String PROP_UPDATER_URI = "lineage.updater.uri"; 51 | 52 | public static final String PREF_INSTALL_OLD_TIMESTAMP = "install_old_timestamp"; 53 | public static final String PREF_INSTALL_NEW_TIMESTAMP = "install_new_timestamp"; 54 | public static final String PREF_INSTALL_PACKAGE_PATH = "install_package_path"; 55 | public static final String PREF_INSTALL_AGAIN = "install_again"; 56 | public static final String PREF_INSTALL_NOTIFIED = "install_notified"; 57 | 58 | public static final String UPDATE_RECOVERY_EXEC = "/vendor/bin/install-recovery.sh"; 59 | public static final String UPDATE_RECOVERY_PROPERTY = "persist.vendor.recovery_update"; 60 | 61 | public static final String HAS_SEEN_INFO_DIALOG = "has_seen_info_dialog"; 62 | public static final String HAS_SEEN_WELCOME_MESSAGE = "has_seen_welcome_message"; 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/java/org/lineageos/updater/misc/FileUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2022 The LineageOS Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.lineageos.updater.misc; 17 | 18 | import android.content.ContentResolver; 19 | import android.database.Cursor; 20 | import android.net.Uri; 21 | import android.os.ParcelFileDescriptor; 22 | import android.provider.OpenableColumns; 23 | import android.util.Log; 24 | 25 | import androidx.annotation.NonNull; 26 | 27 | import java.io.File; 28 | import java.io.FileInputStream; 29 | import java.io.FileOutputStream; 30 | import java.io.IOException; 31 | import java.nio.ByteBuffer; 32 | import java.nio.channels.FileChannel; 33 | import java.nio.channels.ReadableByteChannel; 34 | 35 | public class FileUtils { 36 | 37 | private static final String TAG = "FileUtils"; 38 | 39 | public interface ProgressCallBack { 40 | void update(int progress); 41 | } 42 | 43 | private static class CallbackByteChannel implements ReadableByteChannel { 44 | private final ProgressCallBack mCallback; 45 | private final long mSize; 46 | private final ReadableByteChannel mReadableByteChannel; 47 | private long mSizeRead; 48 | private int mProgress; 49 | 50 | private CallbackByteChannel(ReadableByteChannel readableByteChannel, long expectedSize, 51 | ProgressCallBack callback) { 52 | this.mCallback = callback; 53 | this.mSize = expectedSize; 54 | this.mReadableByteChannel = readableByteChannel; 55 | } 56 | 57 | @Override 58 | public void close() throws IOException { 59 | mReadableByteChannel.close(); 60 | } 61 | 62 | @Override 63 | public boolean isOpen() { 64 | return mReadableByteChannel.isOpen(); 65 | } 66 | 67 | @Override 68 | public int read(ByteBuffer bb) throws IOException { 69 | int read; 70 | if ((read = mReadableByteChannel.read(bb)) > 0) { 71 | mSizeRead += read; 72 | int progress = mSize > 0 ? Math.round(mSizeRead * 100.f / mSize) : -1; 73 | if (mProgress != progress) { 74 | mCallback.update(progress); 75 | mProgress = progress; 76 | } 77 | } 78 | return read; 79 | } 80 | } 81 | 82 | public static void copyFile(File sourceFile, File destFile, ProgressCallBack progressCallBack) 83 | throws IOException { 84 | try (FileChannel sourceChannel = new FileInputStream(sourceFile).getChannel(); 85 | FileChannel destChannel = new FileOutputStream(destFile).getChannel()) { 86 | if (progressCallBack != null) { 87 | ReadableByteChannel readableByteChannel = new CallbackByteChannel(sourceChannel, 88 | sourceFile.length(), progressCallBack); 89 | destChannel.transferFrom(readableByteChannel, 0, sourceChannel.size()); 90 | } else { 91 | destChannel.transferFrom(sourceChannel, 0, sourceChannel.size()); 92 | } 93 | } catch (IOException e) { 94 | Log.e(TAG, "Could not copy file", e); 95 | if (destFile.exists()) { 96 | //noinspection ResultOfMethodCallIgnored 97 | destFile.delete(); 98 | } 99 | throw e; 100 | } 101 | } 102 | 103 | public static void copyFile(ContentResolver cr, File sourceFile, Uri destUri, 104 | ProgressCallBack progressCallBack) throws IOException { 105 | try (FileChannel sourceChannel = new FileInputStream(sourceFile).getChannel(); 106 | ParcelFileDescriptor pfd = cr.openFileDescriptor(destUri, "w"); 107 | FileChannel destChannel = new FileOutputStream(pfd.getFileDescriptor()).getChannel()) { 108 | if (progressCallBack != null) { 109 | ReadableByteChannel readableByteChannel = new CallbackByteChannel(sourceChannel, 110 | sourceFile.length(), progressCallBack); 111 | destChannel.transferFrom(readableByteChannel, 0, sourceChannel.size()); 112 | } else { 113 | destChannel.transferFrom(sourceChannel, 0, sourceChannel.size()); 114 | } 115 | } catch (IOException e) { 116 | Log.e(TAG, "Could not copy file", e); 117 | throw e; 118 | } 119 | } 120 | 121 | public static String queryName(@NonNull ContentResolver resolver, Uri uri) { 122 | try (Cursor returnCursor = resolver.query(uri, null, null, null, null)) { 123 | returnCursor.moveToFirst(); 124 | int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); 125 | return returnCursor.getString(nameIndex); 126 | } catch (Exception e) { 127 | // ignore 128 | return null; 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /app/src/main/java/org/lineageos/updater/misc/StringGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2022 The LineageOS Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.lineageos.updater.misc; 17 | 18 | import android.content.Context; 19 | import android.content.res.Resources; 20 | 21 | import org.lineageos.updater.R; 22 | 23 | import java.text.DateFormat; 24 | import java.util.Date; 25 | import java.util.Locale; 26 | import java.util.TimeZone; 27 | 28 | public final class StringGenerator { 29 | 30 | private StringGenerator() { 31 | } 32 | 33 | public static String getTimeLocalized(Context context, long unixTimestamp) { 34 | DateFormat f = DateFormat.getTimeInstance(DateFormat.SHORT, getCurrentLocale(context)); 35 | Date date = new Date(unixTimestamp * 1000); 36 | return f.format(date); 37 | } 38 | 39 | public static String getDateLocalized(Context context, int dateFormat, long unixTimestamp) { 40 | DateFormat f = DateFormat.getDateInstance(dateFormat, getCurrentLocale(context)); 41 | Date date = new Date(unixTimestamp * 1000); 42 | return f.format(date); 43 | } 44 | 45 | public static String getDateLocalizedUTC(Context context, int dateFormat, long unixTimestamp) { 46 | DateFormat f = DateFormat.getDateInstance(dateFormat, getCurrentLocale(context)); 47 | f.setTimeZone(TimeZone.getTimeZone("UTC")); 48 | Date date = new Date(unixTimestamp * 1000); 49 | return f.format(date); 50 | } 51 | 52 | public static String formatETA(Context context, long millis) { 53 | final long SECOND_IN_MILLIS = 1000; 54 | final long MINUTE_IN_MILLIS = SECOND_IN_MILLIS * 60; 55 | final long HOUR_IN_MILLIS = MINUTE_IN_MILLIS * 60; 56 | Resources res = context.getResources(); 57 | if (millis >= HOUR_IN_MILLIS) { 58 | final int hours = (int) ((millis + 1800000) / HOUR_IN_MILLIS); 59 | return res.getQuantityString(R.plurals.eta_hours, hours, hours); 60 | } else if (millis >= MINUTE_IN_MILLIS) { 61 | final int minutes = (int) ((millis + 30000) / MINUTE_IN_MILLIS); 62 | return res.getQuantityString(R.plurals.eta_minutes, minutes, minutes); 63 | } else { 64 | final int seconds = (int) ((millis + 500) / SECOND_IN_MILLIS); 65 | return res.getQuantityString(R.plurals.eta_seconds, seconds, seconds); 66 | } 67 | } 68 | 69 | public static Locale getCurrentLocale(Context context) { 70 | return context.getResources().getConfiguration().getLocales() 71 | .getFirstMatch(context.getResources().getAssets().getLocales()); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/org/lineageos/updater/model/Update.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The LineageOS Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.lineageos.updater.model; 17 | 18 | import java.io.File; 19 | 20 | public class Update extends UpdateBase implements UpdateInfo { 21 | public static final String LOCAL_ID = "local"; 22 | 23 | private UpdateStatus mStatus = UpdateStatus.UNKNOWN; 24 | private int mPersistentStatus = UpdateStatus.Persistent.UNKNOWN; 25 | private File mFile; 26 | private int mProgress; 27 | private long mEta; 28 | private long mSpeed; 29 | private int mInstallProgress; 30 | private boolean mAvailableOnline; 31 | private boolean mIsFinalizing; 32 | 33 | public Update() { 34 | } 35 | 36 | public Update(UpdateInfo update) { 37 | super(update); 38 | mStatus = update.getStatus(); 39 | mPersistentStatus = update.getPersistentStatus(); 40 | mFile = update.getFile(); 41 | mProgress = update.getProgress(); 42 | mEta = update.getEta(); 43 | mSpeed = update.getSpeed(); 44 | mInstallProgress = update.getInstallProgress(); 45 | mAvailableOnline = update.getAvailableOnline(); 46 | mIsFinalizing = update.getFinalizing(); 47 | } 48 | 49 | @Override 50 | public UpdateStatus getStatus() { 51 | return mStatus; 52 | } 53 | 54 | public void setStatus(UpdateStatus status) { 55 | mStatus = status; 56 | } 57 | 58 | @Override 59 | public int getPersistentStatus() { 60 | return mPersistentStatus; 61 | } 62 | 63 | public void setPersistentStatus(int status) { 64 | mPersistentStatus = status; 65 | } 66 | 67 | @Override 68 | public File getFile() { 69 | return mFile; 70 | } 71 | 72 | public void setFile(File file) { 73 | mFile = file; 74 | } 75 | 76 | @Override 77 | public int getProgress() { 78 | return mProgress; 79 | } 80 | 81 | public void setProgress(int progress) { 82 | mProgress = progress; 83 | } 84 | 85 | @Override 86 | public long getEta() { 87 | return mEta; 88 | } 89 | 90 | public void setEta(long eta) { 91 | mEta = eta; 92 | } 93 | 94 | @Override 95 | public long getSpeed() { 96 | return mSpeed; 97 | } 98 | 99 | public void setSpeed(long speed) { 100 | mSpeed = speed; 101 | } 102 | 103 | @Override 104 | public int getInstallProgress() { 105 | return mInstallProgress; 106 | } 107 | 108 | public void setInstallProgress(int progress) { 109 | mInstallProgress = progress; 110 | } 111 | 112 | @Override 113 | public boolean getAvailableOnline() { 114 | return mAvailableOnline; 115 | } 116 | 117 | public void setAvailableOnline(boolean availableOnline) { 118 | mAvailableOnline = availableOnline; 119 | } 120 | 121 | @Override 122 | public boolean getFinalizing() { 123 | return mIsFinalizing; 124 | } 125 | 126 | public void setFinalizing(boolean finalizing) { 127 | mIsFinalizing = finalizing; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /app/src/main/java/org/lineageos/updater/model/UpdateBase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The LineageOS Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.lineageos.updater.model; 17 | 18 | public class UpdateBase implements UpdateBaseInfo { 19 | 20 | private String mName; 21 | private String mDownloadUrl; 22 | private String mDownloadId; 23 | private long mTimestamp; 24 | private String mType; 25 | private String mVersion; 26 | private long mFileSize; 27 | 28 | public UpdateBase() { 29 | } 30 | 31 | public UpdateBase(UpdateBaseInfo update) { 32 | mName = update.getName(); 33 | mDownloadUrl = update.getDownloadUrl(); 34 | mDownloadId = update.getDownloadId(); 35 | mTimestamp = update.getTimestamp(); 36 | mType = update.getType(); 37 | mVersion = update.getVersion(); 38 | mFileSize = update.getFileSize(); 39 | } 40 | 41 | @Override 42 | public String getName() { 43 | return mName; 44 | } 45 | 46 | public void setName(String name) { 47 | mName = name; 48 | } 49 | 50 | @Override 51 | public String getDownloadId() { 52 | return mDownloadId; 53 | } 54 | 55 | public void setDownloadId(String downloadId) { 56 | mDownloadId = downloadId; 57 | } 58 | 59 | @Override 60 | public long getTimestamp() { 61 | return mTimestamp; 62 | } 63 | 64 | public void setTimestamp(long timestamp) { 65 | mTimestamp = timestamp; 66 | } 67 | 68 | @Override 69 | public String getType() { 70 | return mType; 71 | } 72 | 73 | public void setType(String type) { 74 | mType = type; 75 | } 76 | 77 | @Override 78 | public String getVersion() { 79 | return mVersion; 80 | } 81 | 82 | public void setVersion(String version) { 83 | mVersion = version; 84 | } 85 | 86 | @Override 87 | public String getDownloadUrl() { 88 | return mDownloadUrl; 89 | } 90 | 91 | public void setDownloadUrl(String downloadUrl) { 92 | mDownloadUrl = downloadUrl; 93 | } 94 | 95 | @Override 96 | public long getFileSize() { 97 | return mFileSize; 98 | } 99 | 100 | public void setFileSize(long fileSize) { 101 | mFileSize = fileSize; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /app/src/main/java/org/lineageos/updater/model/UpdateBaseInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The LineageOS Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.lineageos.updater.model; 17 | 18 | public interface UpdateBaseInfo { 19 | String getName(); 20 | 21 | String getDownloadId(); 22 | 23 | long getTimestamp(); 24 | 25 | String getType(); 26 | 27 | String getVersion(); 28 | 29 | String getDownloadUrl(); 30 | 31 | long getFileSize(); 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/org/lineageos/updater/model/UpdateInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The LineageOS Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.lineageos.updater.model; 17 | 18 | import java.io.File; 19 | 20 | public interface UpdateInfo extends UpdateBaseInfo { 21 | UpdateStatus getStatus(); 22 | 23 | int getPersistentStatus(); 24 | 25 | File getFile(); 26 | 27 | long getFileSize(); 28 | 29 | int getProgress(); 30 | 31 | long getEta(); 32 | 33 | long getSpeed(); 34 | 35 | int getInstallProgress(); 36 | 37 | boolean getAvailableOnline(); 38 | 39 | boolean getFinalizing(); 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/org/lineageos/updater/model/UpdateStatus.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The LineageOS Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.lineageos.updater.model; 17 | 18 | public enum UpdateStatus { 19 | UNKNOWN, 20 | STARTING, 21 | DOWNLOADING, 22 | PAUSED, 23 | PAUSED_ERROR, 24 | DELETED, 25 | VERIFYING, 26 | VERIFIED, 27 | VERIFICATION_FAILED, 28 | INSTALLING, 29 | INSTALLED, 30 | INSTALLATION_FAILED, 31 | INSTALLATION_CANCELLED, 32 | INSTALLATION_SUSPENDED; 33 | 34 | public static final class Persistent { 35 | public static final int UNKNOWN = 0; 36 | public static final int INCOMPLETE = 1; 37 | public static final int VERIFIED = 2; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 11 | 14 | 17 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 12 | 13 | 15 | 19 | 22 | 23 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_menu_preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_menu_refresh.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_pause.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_system_update.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout-television/activity_updates.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 21 | 22 | 26 | 27 | 36 | 37 | 44 | 45 | 52 | 53 | 60 | 61 | 62 | 68 | 69 |