blob: 9bab1319d504926c0f5a09bdbef08258ed737c68 [file] [log] [blame]
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -07001/*
2 * Copyright (C) 2018 The Android Open Source 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
17package com.example.android.systemupdatersample.ui;
18
19import android.app.Activity;
20import android.app.AlertDialog;
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -070021import android.graphics.Color;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070022import android.os.Build;
23import android.os.Bundle;
24import android.os.UpdateEngine;
25import android.os.UpdateEngineCallback;
26import android.util.Log;
27import android.view.View;
28import android.widget.ArrayAdapter;
29import android.widget.Button;
30import android.widget.ProgressBar;
31import android.widget.Spinner;
32import android.widget.TextView;
33import android.widget.Toast;
34
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -070035import com.example.android.systemupdatersample.PayloadSpec;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070036import com.example.android.systemupdatersample.R;
37import com.example.android.systemupdatersample.UpdateConfig;
Zhomart Mukhamejanov0dd5a832018-04-23 11:38:54 -070038import com.example.android.systemupdatersample.services.PrepareStreamingService;
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -070039import com.example.android.systemupdatersample.util.PayloadSpecs;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070040import com.example.android.systemupdatersample.util.UpdateConfigs;
41import com.example.android.systemupdatersample.util.UpdateEngineErrorCodes;
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -070042import com.example.android.systemupdatersample.util.UpdateEngineProperties;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070043import com.example.android.systemupdatersample.util.UpdateEngineStatuses;
44
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -070045import java.io.IOException;
Zhomart Mukhamejanov6aa5fb02018-05-09 14:28:49 -070046import java.util.ArrayList;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070047import java.util.List;
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -070048import java.util.concurrent.atomic.AtomicBoolean;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070049import java.util.concurrent.atomic.AtomicInteger;
50
51/**
52 * UI for SystemUpdaterSample app.
53 */
54public class MainActivity extends Activity {
55
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -070056 private static final String TAG = "MainActivity";
57
Zhomart Mukhamejanov6aa5fb02018-05-09 14:28:49 -070058 /** HTTP Header: User-Agent; it will be sent to the server when streaming the payload. */
59 private static final String HTTP_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
60 + "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36";
61
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070062 private TextView mTextViewBuild;
63 private Spinner mSpinnerConfigs;
64 private TextView mTextViewConfigsDirHint;
65 private Button mButtonReload;
66 private Button mButtonApplyConfig;
67 private Button mButtonStop;
68 private Button mButtonReset;
69 private ProgressBar mProgressBar;
70 private TextView mTextViewStatus;
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -070071 private TextView mTextViewCompletion;
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -070072 private TextView mTextViewUpdateInfo;
73 private Button mButtonSwitchSlot;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070074
75 private List<UpdateConfig> mConfigs;
76 private AtomicInteger mUpdateEngineStatus =
77 new AtomicInteger(UpdateEngine.UpdateStatusConstants.IDLE);
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -070078 private PayloadSpec mLastPayloadSpec;
79 private AtomicBoolean mManualSwitchSlotRequired = new AtomicBoolean(true);
Zhomart Mukhamejanovb0361ff2018-05-18 10:22:13 -070080 private final PayloadSpecs mPayloadSpecs = new PayloadSpecs();
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070081
82 /**
83 * Listen to {@code update_engine} events.
84 */
85 private UpdateEngineCallbackImpl mUpdateEngineCallback = new UpdateEngineCallbackImpl();
86
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -070087 private final UpdateEngine mUpdateEngine = new UpdateEngine();
88
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070089 @Override
90 protected void onCreate(Bundle savedInstanceState) {
91 super.onCreate(savedInstanceState);
92 setContentView(R.layout.activity_main);
93
94 this.mTextViewBuild = findViewById(R.id.textViewBuild);
95 this.mSpinnerConfigs = findViewById(R.id.spinnerConfigs);
96 this.mTextViewConfigsDirHint = findViewById(R.id.textViewConfigsDirHint);
97 this.mButtonReload = findViewById(R.id.buttonReload);
98 this.mButtonApplyConfig = findViewById(R.id.buttonApplyConfig);
99 this.mButtonStop = findViewById(R.id.buttonStop);
100 this.mButtonReset = findViewById(R.id.buttonReset);
101 this.mProgressBar = findViewById(R.id.progressBar);
102 this.mTextViewStatus = findViewById(R.id.textViewStatus);
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700103 this.mTextViewCompletion = findViewById(R.id.textViewCompletion);
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700104 this.mTextViewUpdateInfo = findViewById(R.id.textViewUpdateInfo);
105 this.mButtonSwitchSlot = findViewById(R.id.buttonSwitchSlot);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700106
107 this.mTextViewConfigsDirHint.setText(UpdateConfigs.getConfigsRoot(this));
108
109 uiReset();
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700110 loadUpdateConfigs();
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700111
112 this.mUpdateEngine.bind(mUpdateEngineCallback);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700113 }
114
115 @Override
116 protected void onDestroy() {
117 this.mUpdateEngine.unbind();
118 super.onDestroy();
119 }
120
121 /**
122 * reload button is clicked
123 */
124 public void onReloadClick(View view) {
125 loadUpdateConfigs();
126 }
127
128 /**
129 * view config button is clicked
130 */
131 public void onViewConfigClick(View view) {
132 UpdateConfig config = mConfigs.get(mSpinnerConfigs.getSelectedItemPosition());
133 new AlertDialog.Builder(this)
134 .setTitle(config.getName())
135 .setMessage(config.getRawJson())
136 .setPositiveButton(R.string.close, (dialog, id) -> dialog.dismiss())
137 .show();
138 }
139
140 /**
141 * apply config button is clicked
142 */
143 public void onApplyConfigClick(View view) {
144 new AlertDialog.Builder(this)
145 .setTitle("Apply Update")
146 .setMessage("Do you really want to apply this update?")
147 .setIcon(android.R.drawable.ic_dialog_alert)
148 .setPositiveButton(android.R.string.ok, (dialog, whichButton) -> {
149 uiSetUpdating();
150 applyUpdate(getSelectedConfig());
151 })
152 .setNegativeButton(android.R.string.cancel, null)
153 .show();
154 }
155
156 /**
157 * stop button clicked
158 */
159 public void onStopClick(View view) {
160 new AlertDialog.Builder(this)
161 .setTitle("Stop Update")
162 .setMessage("Do you really want to cancel running update?")
163 .setIcon(android.R.drawable.ic_dialog_alert)
164 .setPositiveButton(android.R.string.ok, (dialog, whichButton) -> {
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700165 stopRunningUpdate();
166 })
167 .setNegativeButton(android.R.string.cancel, null).show();
168 }
169
170 /**
171 * reset button clicked
172 */
173 public void onResetClick(View view) {
174 new AlertDialog.Builder(this)
175 .setTitle("Reset Update")
176 .setMessage("Do you really want to cancel running update"
177 + " and restore old version?")
178 .setIcon(android.R.drawable.ic_dialog_alert)
179 .setPositiveButton(android.R.string.ok, (dialog, whichButton) -> {
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700180 resetUpdate();
181 })
182 .setNegativeButton(android.R.string.cancel, null).show();
183 }
184
185 /**
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700186 * switch slot button clicked
187 */
188 public void onSwitchSlotClick(View view) {
189 setSwitchSlotOnReboot();
190 }
191
192 /**
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700193 * Invoked when anything changes. The value of {@code status} will
194 * be one of the values from {@link UpdateEngine.UpdateStatusConstants},
195 * and {@code percent} will be from {@code 0.0} to {@code 1.0}.
196 */
197 private void onStatusUpdate(int status, float percent) {
198 mProgressBar.setProgress((int) (100 * percent));
199 if (mUpdateEngineStatus.get() != status) {
200 mUpdateEngineStatus.set(status);
201 runOnUiThread(() -> {
202 Log.e("UpdateEngine", "StatusUpdate - status="
203 + UpdateEngineStatuses.getStatusText(status)
204 + "/" + status);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700205 Toast.makeText(this, "Update Status changed", Toast.LENGTH_LONG)
206 .show();
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700207 if (status == UpdateEngine.UpdateStatusConstants.IDLE) {
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700208 Log.d(TAG, "status changed, resetting ui");
209 uiReset();
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700210 } else {
211 Log.d(TAG, "status changed, setting ui to updating mode");
212 uiSetUpdating();
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700213 }
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700214 setUiStatus(status);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700215 });
216 }
217 }
218
219 /**
220 * Invoked when the payload has been applied, whether successfully or
221 * unsuccessfully. The value of {@code errorCode} will be one of the
222 * values from {@link UpdateEngine.ErrorCodeConstants}.
223 */
224 private void onPayloadApplicationComplete(int errorCode) {
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700225 final String state = UpdateEngineErrorCodes.isUpdateSucceeded(errorCode)
226 ? "SUCCESS"
227 : "FAILURE";
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700228 runOnUiThread(() -> {
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700229 Log.i("UpdateEngine",
230 "Completed - errorCode="
231 + UpdateEngineErrorCodes.getCodeName(errorCode) + "/" + errorCode
232 + " " + state);
233 Toast.makeText(this, "Update completed", Toast.LENGTH_LONG).show();
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700234 setUiCompletion(errorCode);
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700235 if (errorCode == UpdateEngineErrorCodes.UPDATED_BUT_NOT_ACTIVE) {
236 // if update was successfully applied.
237 if (mManualSwitchSlotRequired.get()) {
238 // Show "Switch Slot" button.
239 uiShowSwitchSlotInfo();
240 }
241 }
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700242 });
243 }
244
245 /** resets ui */
246 private void uiReset() {
247 mTextViewBuild.setText(Build.DISPLAY);
248 mSpinnerConfigs.setEnabled(true);
249 mButtonReload.setEnabled(true);
250 mButtonApplyConfig.setEnabled(true);
251 mButtonStop.setEnabled(false);
252 mButtonReset.setEnabled(false);
253 mProgressBar.setProgress(0);
254 mProgressBar.setEnabled(false);
255 mProgressBar.setVisibility(ProgressBar.INVISIBLE);
256 mTextViewStatus.setText(R.string.unknown);
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700257 mTextViewCompletion.setText(R.string.unknown);
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700258 uiHideSwitchSlotInfo();
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700259 }
260
261 /** sets ui updating mode */
262 private void uiSetUpdating() {
263 mTextViewBuild.setText(Build.DISPLAY);
264 mSpinnerConfigs.setEnabled(false);
265 mButtonReload.setEnabled(false);
266 mButtonApplyConfig.setEnabled(false);
267 mButtonStop.setEnabled(true);
268 mProgressBar.setEnabled(true);
269 mButtonReset.setEnabled(true);
270 mProgressBar.setVisibility(ProgressBar.VISIBLE);
271 }
272
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700273 private void uiShowSwitchSlotInfo() {
274 mButtonSwitchSlot.setEnabled(true);
275 mTextViewUpdateInfo.setTextColor(Color.parseColor("#777777"));
276 }
277
278 private void uiHideSwitchSlotInfo() {
279 mTextViewUpdateInfo.setTextColor(Color.parseColor("#AAAAAA"));
280 mButtonSwitchSlot.setEnabled(false);
281 }
282
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700283 /**
284 * loads json configurations from configs dir that is defined in {@link UpdateConfigs}.
285 */
286 private void loadUpdateConfigs() {
287 mConfigs = UpdateConfigs.getUpdateConfigs(this);
288 loadConfigsToSpinner(mConfigs);
289 }
290
291 /**
292 * @param status update engine status code
293 */
294 private void setUiStatus(int status) {
295 String statusText = UpdateEngineStatuses.getStatusText(status);
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700296 mTextViewStatus.setText(statusText + "/" + status);
297 }
298
299 /**
300 * @param errorCode update engine error code
301 */
302 private void setUiCompletion(int errorCode) {
303 final String state = UpdateEngineErrorCodes.isUpdateSucceeded(errorCode)
304 ? "SUCCESS"
305 : "FAILURE";
306 String errorText = UpdateEngineErrorCodes.getCodeName(errorCode);
307 mTextViewCompletion.setText(state + " " + errorText + "/" + errorCode);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700308 }
309
310 private void loadConfigsToSpinner(List<UpdateConfig> configs) {
311 String[] spinnerArray = UpdateConfigs.configsToNames(configs);
312 ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(this,
313 android.R.layout.simple_spinner_item,
314 spinnerArray);
315 spinnerArrayAdapter.setDropDownViewResource(android.R.layout
316 .simple_spinner_dropdown_item);
317 mSpinnerConfigs.setAdapter(spinnerArrayAdapter);
318 }
319
320 private UpdateConfig getSelectedConfig() {
321 return mConfigs.get(mSpinnerConfigs.getSelectedItemPosition());
322 }
323
324 /**
325 * Applies the given update
326 */
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700327 private void applyUpdate(final UpdateConfig config) {
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700328 List<String> extraProperties = new ArrayList<>();
329
330 if (!config.getAbConfig().getForceSwitchSlot()) {
331 // Disable switch slot on reboot, which is enabled by default.
332 // User will enable it manually by clicking "Switch Slot" button on the screen.
333 extraProperties.add(UpdateEngineProperties.PROPERTY_DISABLE_SWITCH_SLOT_ON_REBOOT);
334 mManualSwitchSlotRequired.set(true);
335 } else {
336 mManualSwitchSlotRequired.set(false);
337 }
338
Zhomart Mukhamejanov963e3ee2018-04-26 21:07:05 -0700339 if (config.getInstallType() == UpdateConfig.AB_INSTALL_TYPE_NON_STREAMING) {
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700340 PayloadSpec payload;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700341 try {
Zhomart Mukhamejanovb0361ff2018-05-18 10:22:13 -0700342 payload = mPayloadSpecs.forNonStreaming(config.getUpdatePackageFile());
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700343 } catch (IOException e) {
344 Log.e(TAG, "Error creating payload spec", e);
345 Toast.makeText(this, "Error creating payload spec", Toast.LENGTH_LONG)
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700346 .show();
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700347 return;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700348 }
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700349 updateEngineApplyPayload(payload, extraProperties);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700350 } else {
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700351 Log.d(TAG, "Starting PrepareStreamingService");
Zhomart Mukhamejanov0dd5a832018-04-23 11:38:54 -0700352 PrepareStreamingService.startService(this, config, (code, payloadSpec) -> {
353 if (code == PrepareStreamingService.RESULT_CODE_SUCCESS) {
Zhomart Mukhamejanov6aa5fb02018-05-09 14:28:49 -0700354 extraProperties.add("USER_AGENT=" + HTTP_USER_AGENT);
355 config.getStreamingMetadata()
356 .getAuthorization()
357 .ifPresent(s -> extraProperties.add("AUTHORIZATION=" + s));
358 updateEngineApplyPayload(payloadSpec, extraProperties);
Zhomart Mukhamejanov0dd5a832018-04-23 11:38:54 -0700359 } else {
360 Log.e(TAG, "PrepareStreamingService failed, result code is " + code);
361 Toast.makeText(
362 MainActivity.this,
363 "PrepareStreamingService failed, result code is " + code,
364 Toast.LENGTH_LONG).show();
365 }
366 });
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700367 }
368 }
369
370 /**
371 * Applies given payload.
372 *
373 * UpdateEngine works asynchronously. This method doesn't wait until
374 * end of the update.
Zhomart Mukhamejanov6aa5fb02018-05-09 14:28:49 -0700375 *
376 * @param payloadSpec contains url, offset and size to {@code PAYLOAD_BINARY_FILE_NAME}
377 * @param extraProperties additional properties to pass to {@link UpdateEngine#applyPayload}
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700378 */
Zhomart Mukhamejanov6aa5fb02018-05-09 14:28:49 -0700379 private void updateEngineApplyPayload(PayloadSpec payloadSpec, List<String> extraProperties) {
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700380 mLastPayloadSpec = payloadSpec;
381
Zhomart Mukhamejanov6aa5fb02018-05-09 14:28:49 -0700382 ArrayList<String> properties = new ArrayList<>(payloadSpec.getProperties());
383 if (extraProperties != null) {
384 properties.addAll(extraProperties);
385 }
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700386 try {
387 mUpdateEngine.applyPayload(
388 payloadSpec.getUrl(),
389 payloadSpec.getOffset(),
390 payloadSpec.getSize(),
Zhomart Mukhamejanov6aa5fb02018-05-09 14:28:49 -0700391 properties.toArray(new String[0]));
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700392 } catch (Exception e) {
393 Log.e(TAG, "UpdateEngine failed to apply the update", e);
394 Toast.makeText(
395 this,
396 "UpdateEngine failed to apply the update",
397 Toast.LENGTH_LONG).show();
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700398 }
399 }
400
401 /**
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700402 * Sets the new slot that has the updated partitions as the active slot,
403 * which device will boot into next time.
404 * This method is only supposed to be called after the payload is applied.
405 *
406 * Invoking {@link UpdateEngine#applyPayload} with the same payload url, offset, size
407 * and payload metadata headers doesn't trigger new update. It can be used to just switch
408 * active A/B slot.
409 *
410 * {@link UpdateEngine#applyPayload} might take several seconds to finish, and it will
411 * invoke callbacks {@link this#onStatusUpdate} and {@link this#onPayloadApplicationComplete)}.
412 */
413 private void setSwitchSlotOnReboot() {
414 Log.d(TAG, "setSwitchSlotOnReboot invoked");
415 List<String> extraProperties = new ArrayList<>();
416 // PROPERTY_SKIP_POST_INSTALL should be passed on to skip post-installation hooks.
417 extraProperties.add(UpdateEngineProperties.PROPERTY_SKIP_POST_INSTALL);
418 // It sets property SWITCH_SLOT_ON_REBOOT=1 by default.
419 // HTTP headers are not required, UpdateEngine is not expected to stream payload.
420 updateEngineApplyPayload(mLastPayloadSpec, extraProperties);
421 uiHideSwitchSlotInfo();
422 }
423
424 /**
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700425 * Requests update engine to stop any ongoing update. If an update has been applied,
426 * leave it as is.
427 */
428 private void stopRunningUpdate() {
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700429 try {
430 mUpdateEngine.cancel();
431 } catch (Exception e) {
432 Log.w(TAG, "UpdateEngine failed to stop the ongoing update", e);
433 }
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700434 }
435
436 /**
437 * Resets update engine to IDLE state. Requests to cancel any onging update, or to revert if an
438 * update has been applied.
439 */
440 private void resetUpdate() {
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700441 try {
442 mUpdateEngine.resetStatus();
443 } catch (Exception e) {
444 Log.w(TAG, "UpdateEngine failed to reset the update", e);
445 }
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700446 }
447
448 /**
Zhomart Mukhamejanovda7e2372018-05-01 12:50:55 -0700449 * Helper class to delegate {@code update_engine} callbacks to MainActivity
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700450 */
451 class UpdateEngineCallbackImpl extends UpdateEngineCallback {
452 @Override
453 public void onStatusUpdate(int status, float percent) {
454 MainActivity.this.onStatusUpdate(status, percent);
455 }
456
457 @Override
458 public void onPayloadApplicationComplete(int errorCode) {
459 MainActivity.this.onPayloadApplicationComplete(errorCode);
460 }
461 }
462
463}