blob: 359e2b10c5e21126a3980c44c9203c27e024a4ad [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;
21import android.os.Build;
22import android.os.Bundle;
23import android.os.UpdateEngine;
24import android.os.UpdateEngineCallback;
25import android.util.Log;
26import android.view.View;
27import android.widget.ArrayAdapter;
28import android.widget.Button;
29import android.widget.ProgressBar;
30import android.widget.Spinner;
31import android.widget.TextView;
32import android.widget.Toast;
33
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -070034import com.example.android.systemupdatersample.PayloadSpec;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070035import com.example.android.systemupdatersample.R;
36import com.example.android.systemupdatersample.UpdateConfig;
Zhomart Mukhamejanov0dd5a832018-04-23 11:38:54 -070037import com.example.android.systemupdatersample.services.PrepareStreamingService;
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -070038import com.example.android.systemupdatersample.util.PayloadSpecs;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070039import com.example.android.systemupdatersample.util.UpdateConfigs;
40import com.example.android.systemupdatersample.util.UpdateEngineErrorCodes;
41import com.example.android.systemupdatersample.util.UpdateEngineStatuses;
42
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -070043import java.io.IOException;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070044import java.util.List;
45import java.util.concurrent.atomic.AtomicInteger;
46
47/**
48 * UI for SystemUpdaterSample app.
49 */
50public class MainActivity extends Activity {
51
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -070052 private static final String TAG = "MainActivity";
53
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070054 private TextView mTextViewBuild;
55 private Spinner mSpinnerConfigs;
56 private TextView mTextViewConfigsDirHint;
57 private Button mButtonReload;
58 private Button mButtonApplyConfig;
59 private Button mButtonStop;
60 private Button mButtonReset;
61 private ProgressBar mProgressBar;
62 private TextView mTextViewStatus;
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -070063 private TextView mTextViewCompletion;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070064
65 private List<UpdateConfig> mConfigs;
66 private AtomicInteger mUpdateEngineStatus =
67 new AtomicInteger(UpdateEngine.UpdateStatusConstants.IDLE);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070068
69 /**
70 * Listen to {@code update_engine} events.
71 */
72 private UpdateEngineCallbackImpl mUpdateEngineCallback = new UpdateEngineCallbackImpl();
73
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -070074 private final UpdateEngine mUpdateEngine = new UpdateEngine();
75
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070076 @Override
77 protected void onCreate(Bundle savedInstanceState) {
78 super.onCreate(savedInstanceState);
79 setContentView(R.layout.activity_main);
80
81 this.mTextViewBuild = findViewById(R.id.textViewBuild);
82 this.mSpinnerConfigs = findViewById(R.id.spinnerConfigs);
83 this.mTextViewConfigsDirHint = findViewById(R.id.textViewConfigsDirHint);
84 this.mButtonReload = findViewById(R.id.buttonReload);
85 this.mButtonApplyConfig = findViewById(R.id.buttonApplyConfig);
86 this.mButtonStop = findViewById(R.id.buttonStop);
87 this.mButtonReset = findViewById(R.id.buttonReset);
88 this.mProgressBar = findViewById(R.id.progressBar);
89 this.mTextViewStatus = findViewById(R.id.textViewStatus);
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -070090 this.mTextViewCompletion = findViewById(R.id.textViewCompletion);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070091
92 this.mTextViewConfigsDirHint.setText(UpdateConfigs.getConfigsRoot(this));
93
94 uiReset();
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070095 loadUpdateConfigs();
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -070096
97 this.mUpdateEngine.bind(mUpdateEngineCallback);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070098 }
99
100 @Override
101 protected void onDestroy() {
102 this.mUpdateEngine.unbind();
103 super.onDestroy();
104 }
105
106 /**
107 * reload button is clicked
108 */
109 public void onReloadClick(View view) {
110 loadUpdateConfigs();
111 }
112
113 /**
114 * view config button is clicked
115 */
116 public void onViewConfigClick(View view) {
117 UpdateConfig config = mConfigs.get(mSpinnerConfigs.getSelectedItemPosition());
118 new AlertDialog.Builder(this)
119 .setTitle(config.getName())
120 .setMessage(config.getRawJson())
121 .setPositiveButton(R.string.close, (dialog, id) -> dialog.dismiss())
122 .show();
123 }
124
125 /**
126 * apply config button is clicked
127 */
128 public void onApplyConfigClick(View view) {
129 new AlertDialog.Builder(this)
130 .setTitle("Apply Update")
131 .setMessage("Do you really want to apply this update?")
132 .setIcon(android.R.drawable.ic_dialog_alert)
133 .setPositiveButton(android.R.string.ok, (dialog, whichButton) -> {
134 uiSetUpdating();
135 applyUpdate(getSelectedConfig());
136 })
137 .setNegativeButton(android.R.string.cancel, null)
138 .show();
139 }
140
141 /**
142 * stop button clicked
143 */
144 public void onStopClick(View view) {
145 new AlertDialog.Builder(this)
146 .setTitle("Stop Update")
147 .setMessage("Do you really want to cancel running update?")
148 .setIcon(android.R.drawable.ic_dialog_alert)
149 .setPositiveButton(android.R.string.ok, (dialog, whichButton) -> {
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700150 stopRunningUpdate();
151 })
152 .setNegativeButton(android.R.string.cancel, null).show();
153 }
154
155 /**
156 * reset button clicked
157 */
158 public void onResetClick(View view) {
159 new AlertDialog.Builder(this)
160 .setTitle("Reset Update")
161 .setMessage("Do you really want to cancel running update"
162 + " and restore old version?")
163 .setIcon(android.R.drawable.ic_dialog_alert)
164 .setPositiveButton(android.R.string.ok, (dialog, whichButton) -> {
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700165 resetUpdate();
166 })
167 .setNegativeButton(android.R.string.cancel, null).show();
168 }
169
170 /**
171 * Invoked when anything changes. The value of {@code status} will
172 * be one of the values from {@link UpdateEngine.UpdateStatusConstants},
173 * and {@code percent} will be from {@code 0.0} to {@code 1.0}.
174 */
175 private void onStatusUpdate(int status, float percent) {
176 mProgressBar.setProgress((int) (100 * percent));
177 if (mUpdateEngineStatus.get() != status) {
178 mUpdateEngineStatus.set(status);
179 runOnUiThread(() -> {
180 Log.e("UpdateEngine", "StatusUpdate - status="
181 + UpdateEngineStatuses.getStatusText(status)
182 + "/" + status);
183 setUiStatus(status);
184 Toast.makeText(this, "Update Status changed", Toast.LENGTH_LONG)
185 .show();
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700186 if (status != UpdateEngine.UpdateStatusConstants.IDLE) {
187 Log.d(TAG, "status changed, setting ui to updating mode");
188 uiSetUpdating();
189 } else {
190 Log.d(TAG, "status changed, resetting ui");
191 uiReset();
192 }
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700193 });
194 }
195 }
196
197 /**
198 * Invoked when the payload has been applied, whether successfully or
199 * unsuccessfully. The value of {@code errorCode} will be one of the
200 * values from {@link UpdateEngine.ErrorCodeConstants}.
201 */
202 private void onPayloadApplicationComplete(int errorCode) {
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700203 final String state = UpdateEngineErrorCodes.isUpdateSucceeded(errorCode)
204 ? "SUCCESS"
205 : "FAILURE";
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700206 runOnUiThread(() -> {
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700207 Log.i("UpdateEngine",
208 "Completed - errorCode="
209 + UpdateEngineErrorCodes.getCodeName(errorCode) + "/" + errorCode
210 + " " + state);
211 Toast.makeText(this, "Update completed", Toast.LENGTH_LONG).show();
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700212 setUiCompletion(errorCode);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700213 });
214 }
215
216 /** resets ui */
217 private void uiReset() {
218 mTextViewBuild.setText(Build.DISPLAY);
219 mSpinnerConfigs.setEnabled(true);
220 mButtonReload.setEnabled(true);
221 mButtonApplyConfig.setEnabled(true);
222 mButtonStop.setEnabled(false);
223 mButtonReset.setEnabled(false);
224 mProgressBar.setProgress(0);
225 mProgressBar.setEnabled(false);
226 mProgressBar.setVisibility(ProgressBar.INVISIBLE);
227 mTextViewStatus.setText(R.string.unknown);
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700228 mTextViewCompletion.setText(R.string.unknown);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700229 }
230
231 /** sets ui updating mode */
232 private void uiSetUpdating() {
233 mTextViewBuild.setText(Build.DISPLAY);
234 mSpinnerConfigs.setEnabled(false);
235 mButtonReload.setEnabled(false);
236 mButtonApplyConfig.setEnabled(false);
237 mButtonStop.setEnabled(true);
238 mProgressBar.setEnabled(true);
239 mButtonReset.setEnabled(true);
240 mProgressBar.setVisibility(ProgressBar.VISIBLE);
241 }
242
243 /**
244 * loads json configurations from configs dir that is defined in {@link UpdateConfigs}.
245 */
246 private void loadUpdateConfigs() {
247 mConfigs = UpdateConfigs.getUpdateConfigs(this);
248 loadConfigsToSpinner(mConfigs);
249 }
250
251 /**
252 * @param status update engine status code
253 */
254 private void setUiStatus(int status) {
255 String statusText = UpdateEngineStatuses.getStatusText(status);
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700256 mTextViewStatus.setText(statusText + "/" + status);
257 }
258
259 /**
260 * @param errorCode update engine error code
261 */
262 private void setUiCompletion(int errorCode) {
263 final String state = UpdateEngineErrorCodes.isUpdateSucceeded(errorCode)
264 ? "SUCCESS"
265 : "FAILURE";
266 String errorText = UpdateEngineErrorCodes.getCodeName(errorCode);
267 mTextViewCompletion.setText(state + " " + errorText + "/" + errorCode);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700268 }
269
270 private void loadConfigsToSpinner(List<UpdateConfig> configs) {
271 String[] spinnerArray = UpdateConfigs.configsToNames(configs);
272 ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(this,
273 android.R.layout.simple_spinner_item,
274 spinnerArray);
275 spinnerArrayAdapter.setDropDownViewResource(android.R.layout
276 .simple_spinner_dropdown_item);
277 mSpinnerConfigs.setAdapter(spinnerArrayAdapter);
278 }
279
280 private UpdateConfig getSelectedConfig() {
281 return mConfigs.get(mSpinnerConfigs.getSelectedItemPosition());
282 }
283
284 /**
285 * Applies the given update
286 */
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700287 private void applyUpdate(final UpdateConfig config) {
Zhomart Mukhamejanov963e3ee2018-04-26 21:07:05 -0700288 if (config.getInstallType() == UpdateConfig.AB_INSTALL_TYPE_NON_STREAMING) {
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700289 PayloadSpec payload;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700290 try {
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700291 payload = PayloadSpecs.forNonStreaming(config.getUpdatePackageFile());
292 } catch (IOException e) {
293 Log.e(TAG, "Error creating payload spec", e);
294 Toast.makeText(this, "Error creating payload spec", Toast.LENGTH_LONG)
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700295 .show();
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700296 return;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700297 }
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700298 updateEngineApplyPayload(payload);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700299 } else {
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700300 Log.d(TAG, "Starting PrepareStreamingService");
Zhomart Mukhamejanov0dd5a832018-04-23 11:38:54 -0700301 PrepareStreamingService.startService(this, config, (code, payloadSpec) -> {
302 if (code == PrepareStreamingService.RESULT_CODE_SUCCESS) {
303 updateEngineApplyPayload(payloadSpec);
304 } else {
305 Log.e(TAG, "PrepareStreamingService failed, result code is " + code);
306 Toast.makeText(
307 MainActivity.this,
308 "PrepareStreamingService failed, result code is " + code,
309 Toast.LENGTH_LONG).show();
310 }
311 });
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700312 }
313 }
314
315 /**
316 * Applies given payload.
317 *
318 * UpdateEngine works asynchronously. This method doesn't wait until
319 * end of the update.
320 */
321 private void updateEngineApplyPayload(PayloadSpec payloadSpec) {
322 try {
323 mUpdateEngine.applyPayload(
324 payloadSpec.getUrl(),
325 payloadSpec.getOffset(),
326 payloadSpec.getSize(),
327 payloadSpec.getProperties().toArray(new String[0]));
328 } catch (Exception e) {
329 Log.e(TAG, "UpdateEngine failed to apply the update", e);
330 Toast.makeText(
331 this,
332 "UpdateEngine failed to apply the update",
333 Toast.LENGTH_LONG).show();
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700334 }
335 }
336
337 /**
338 * Requests update engine to stop any ongoing update. If an update has been applied,
339 * leave it as is.
340 */
341 private void stopRunningUpdate() {
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700342 try {
343 mUpdateEngine.cancel();
344 } catch (Exception e) {
345 Log.w(TAG, "UpdateEngine failed to stop the ongoing update", e);
346 }
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700347 }
348
349 /**
350 * Resets update engine to IDLE state. Requests to cancel any onging update, or to revert if an
351 * update has been applied.
352 */
353 private void resetUpdate() {
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700354 try {
355 mUpdateEngine.resetStatus();
356 } catch (Exception e) {
357 Log.w(TAG, "UpdateEngine failed to reset the update", e);
358 }
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700359 }
360
361 /**
Zhomart Mukhamejanovda7e2372018-05-01 12:50:55 -0700362 * Helper class to delegate {@code update_engine} callbacks to MainActivity
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700363 */
364 class UpdateEngineCallbackImpl extends UpdateEngineCallback {
365 @Override
366 public void onStatusUpdate(int status, float percent) {
367 MainActivity.this.onStatusUpdate(status, percent);
368 }
369
370 @Override
371 public void onPayloadApplicationComplete(int errorCode) {
372 MainActivity.this.onPayloadApplicationComplete(errorCode);
373 }
374 }
375
376}