blob: 6c71cb6f46b92a518f6ca4df88f767a3357039a9 [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;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070025import 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;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070032
33import com.example.android.systemupdatersample.R;
34import com.example.android.systemupdatersample.UpdateConfig;
Zhomart Mukhamejanov6f26e712018-05-18 10:15:31 -070035import com.example.android.systemupdatersample.UpdateManager;
Zhomart Mukhamejanov674aa6c2018-05-25 17:00:11 -070036import com.example.android.systemupdatersample.UpdaterState;
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -070037import com.example.android.systemupdatersample.util.PayloadSpecs;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070038import com.example.android.systemupdatersample.util.UpdateConfigs;
39import com.example.android.systemupdatersample.util.UpdateEngineErrorCodes;
40import com.example.android.systemupdatersample.util.UpdateEngineStatuses;
41
42import java.util.List;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070043
44/**
45 * UI for SystemUpdaterSample app.
46 */
47public class MainActivity extends Activity {
48
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -070049 private static final String TAG = "MainActivity";
50
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070051 private TextView mTextViewBuild;
52 private Spinner mSpinnerConfigs;
53 private TextView mTextViewConfigsDirHint;
54 private Button mButtonReload;
55 private Button mButtonApplyConfig;
56 private Button mButtonStop;
57 private Button mButtonReset;
58 private ProgressBar mProgressBar;
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -070059 private TextView mTextViewUpdaterState;
60 private TextView mTextViewEngineStatus;
61 private TextView mTextViewEngineErrorCode;
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -070062 private TextView mTextViewUpdateInfo;
63 private Button mButtonSwitchSlot;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070064
65 private List<UpdateConfig> mConfigs;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070066
Zhomart Mukhamejanov6f26e712018-05-18 10:15:31 -070067 private final UpdateManager mUpdateManager =
68 new UpdateManager(new UpdateEngine(), new PayloadSpecs());
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -070069
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070070 @Override
71 protected void onCreate(Bundle savedInstanceState) {
72 super.onCreate(savedInstanceState);
73 setContentView(R.layout.activity_main);
74
75 this.mTextViewBuild = findViewById(R.id.textViewBuild);
76 this.mSpinnerConfigs = findViewById(R.id.spinnerConfigs);
77 this.mTextViewConfigsDirHint = findViewById(R.id.textViewConfigsDirHint);
78 this.mButtonReload = findViewById(R.id.buttonReload);
79 this.mButtonApplyConfig = findViewById(R.id.buttonApplyConfig);
80 this.mButtonStop = findViewById(R.id.buttonStop);
81 this.mButtonReset = findViewById(R.id.buttonReset);
82 this.mProgressBar = findViewById(R.id.progressBar);
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -070083 this.mTextViewUpdaterState = findViewById(R.id.textViewUpdaterState);
84 this.mTextViewEngineStatus = findViewById(R.id.textViewEngineStatus);
85 this.mTextViewEngineErrorCode = findViewById(R.id.textViewEngineErrorCode);
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -070086 this.mTextViewUpdateInfo = findViewById(R.id.textViewUpdateInfo);
87 this.mButtonSwitchSlot = findViewById(R.id.buttonSwitchSlot);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070088
89 this.mTextViewConfigsDirHint.setText(UpdateConfigs.getConfigsRoot(this));
90
Zhomart Mukhamejanov469b35a2018-06-01 12:41:20 -070091 uiResetWidgets();
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070092 loadUpdateConfigs();
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -070093
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -070094 this.mUpdateManager.setOnStateChangeCallback(this::onUpdaterStateChange);
95 this.mUpdateManager.setOnEngineStatusUpdateCallback(this::onEngineStatusUpdate);
96 this.mUpdateManager.setOnEngineCompleteCallback(this::onEnginePayloadApplicationComplete);
Zhomart Mukhamejanov6f26e712018-05-18 10:15:31 -070097 this.mUpdateManager.setOnProgressUpdateCallback(this::onProgressUpdate);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070098 }
99
100 @Override
101 protected void onDestroy() {
Zhomart Mukhamejanov6f26e712018-05-18 10:15:31 -0700102 this.mUpdateManager.setOnEngineStatusUpdateCallback(null);
103 this.mUpdateManager.setOnProgressUpdateCallback(null);
104 this.mUpdateManager.setOnEngineCompleteCallback(null);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700105 super.onDestroy();
106 }
107
Zhomart Mukhamejanov6f26e712018-05-18 10:15:31 -0700108 @Override
109 protected void onResume() {
110 super.onResume();
Zhomart Mukhamejanov7671f682018-05-24 09:11:47 -0700111 // Binding to UpdateEngine invokes onStatusUpdate callback,
112 // persisted updater state has to be loaded and prepared beforehand.
Zhomart Mukhamejanov6f26e712018-05-18 10:15:31 -0700113 this.mUpdateManager.bind();
114 }
115
116 @Override
117 protected void onPause() {
118 this.mUpdateManager.unbind();
119 super.onPause();
120 }
121
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700122 /**
123 * reload button is clicked
124 */
125 public void onReloadClick(View view) {
126 loadUpdateConfigs();
127 }
128
129 /**
130 * view config button is clicked
131 */
132 public void onViewConfigClick(View view) {
133 UpdateConfig config = mConfigs.get(mSpinnerConfigs.getSelectedItemPosition());
134 new AlertDialog.Builder(this)
135 .setTitle(config.getName())
136 .setMessage(config.getRawJson())
137 .setPositiveButton(R.string.close, (dialog, id) -> dialog.dismiss())
138 .show();
139 }
140
141 /**
142 * apply config button is clicked
143 */
144 public void onApplyConfigClick(View view) {
145 new AlertDialog.Builder(this)
146 .setTitle("Apply Update")
147 .setMessage("Do you really want to apply this update?")
148 .setIcon(android.R.drawable.ic_dialog_alert)
149 .setPositiveButton(android.R.string.ok, (dialog, whichButton) -> {
Zhomart Mukhamejanov469b35a2018-06-01 12:41:20 -0700150 uiResetWidgets();
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700151 uiResetEngineText();
Zhomart Mukhamejanov469b35a2018-06-01 12:41:20 -0700152 applyUpdate(getSelectedConfig());
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700153 })
154 .setNegativeButton(android.R.string.cancel, null)
155 .show();
156 }
157
Zhomart Mukhamejanov469b35a2018-06-01 12:41:20 -0700158 private void applyUpdate(UpdateConfig config) {
159 try {
160 mUpdateManager.applyUpdate(this, config);
161 } catch (UpdaterState.InvalidTransitionException e) {
162 Log.e(TAG, "Failed to apply update " + config.getName(), e);
163 }
164 }
165
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700166 /**
167 * stop button clicked
168 */
169 public void onStopClick(View view) {
170 new AlertDialog.Builder(this)
171 .setTitle("Stop Update")
172 .setMessage("Do you really want to cancel running update?")
173 .setIcon(android.R.drawable.ic_dialog_alert)
174 .setPositiveButton(android.R.string.ok, (dialog, whichButton) -> {
Zhomart Mukhamejanov469b35a2018-06-01 12:41:20 -0700175 cancelRunningUpdate();
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700176 })
177 .setNegativeButton(android.R.string.cancel, null).show();
178 }
179
Zhomart Mukhamejanov469b35a2018-06-01 12:41:20 -0700180 private void cancelRunningUpdate() {
181 try {
182 mUpdateManager.cancelRunningUpdate();
183 } catch (UpdaterState.InvalidTransitionException e) {
184 Log.e(TAG, "Failed to cancel running update", e);
185 }
186 }
187
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700188 /**
189 * reset button clicked
190 */
191 public void onResetClick(View view) {
192 new AlertDialog.Builder(this)
193 .setTitle("Reset Update")
194 .setMessage("Do you really want to cancel running update"
195 + " and restore old version?")
196 .setIcon(android.R.drawable.ic_dialog_alert)
197 .setPositiveButton(android.R.string.ok, (dialog, whichButton) -> {
Zhomart Mukhamejanov469b35a2018-06-01 12:41:20 -0700198 resetUpdate();
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700199 })
200 .setNegativeButton(android.R.string.cancel, null).show();
201 }
202
Zhomart Mukhamejanov469b35a2018-06-01 12:41:20 -0700203 private void resetUpdate() {
204 try {
205 mUpdateManager.resetUpdate();
206 } catch (UpdaterState.InvalidTransitionException e) {
207 Log.e(TAG, "Failed to reset update", e);
208 }
209 }
210
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700211 /**
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700212 * switch slot button clicked
213 */
214 public void onSwitchSlotClick(View view) {
Zhomart Mukhamejanov6f26e712018-05-18 10:15:31 -0700215 mUpdateManager.setSwitchSlotOnReboot();
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700216 }
217
218 /**
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700219 * Invoked when SystemUpdaterSample app state changes.
220 * Value of {@code state} will be one of the
Zhomart Mukhamejanov674aa6c2018-05-25 17:00:11 -0700221 * values from {@link UpdaterState}.
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700222 */
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700223 private void onUpdaterStateChange(int state) {
Zhomart Mukhamejanov469b35a2018-06-01 12:41:20 -0700224 Log.i(TAG, "UpdaterStateChange state="
225 + UpdaterState.getStateText(state)
226 + "/" + state);
Zhomart Mukhamejanov6f26e712018-05-18 10:15:31 -0700227 runOnUiThread(() -> {
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700228 setUiUpdaterState(state);
Zhomart Mukhamejanov469b35a2018-06-01 12:41:20 -0700229
230 if (state == UpdaterState.IDLE) {
231 uiStateIdle();
232 } else if (state == UpdaterState.RUNNING) {
233 uiStateRunning();
234 } else if (state == UpdaterState.PAUSED) {
235 uiStatePaused();
236 } else if (state == UpdaterState.ERROR) {
237 uiStateError();
238 } else if (state == UpdaterState.SLOT_SWITCH_REQUIRED) {
239 uiStateSlotSwitchRequired();
240 } else if (state == UpdaterState.REBOOT_REQUIRED) {
241 uiStateRebootRequired();
242 }
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700243 });
244 }
245
246 /**
247 * Invoked when {@link UpdateEngine} status changes. Value of {@code status} will
248 * be one of the values from {@link UpdateEngine.UpdateStatusConstants}.
249 */
250 private void onEngineStatusUpdate(int status) {
Zhomart Mukhamejanov469b35a2018-06-01 12:41:20 -0700251 Log.i(TAG, "StatusUpdate - status="
252 + UpdateEngineStatuses.getStatusText(status)
253 + "/" + status);
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700254 runOnUiThread(() -> {
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700255 setUiEngineStatus(status);
Zhomart Mukhamejanov6f26e712018-05-18 10:15:31 -0700256 });
257 }
258
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700259 /**
260 * Invoked when the payload has been applied, whether successfully or
261 * unsuccessfully. The value of {@code errorCode} will be one of the
262 * values from {@link UpdateEngine.ErrorCodeConstants}.
263 */
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700264 private void onEnginePayloadApplicationComplete(int errorCode) {
265 final String completionState = UpdateEngineErrorCodes.isUpdateSucceeded(errorCode)
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700266 ? "SUCCESS"
267 : "FAILURE";
Zhomart Mukhamejanov469b35a2018-06-01 12:41:20 -0700268 Log.i(TAG,
269 "PayloadApplicationCompleted - errorCode="
270 + UpdateEngineErrorCodes.getCodeName(errorCode) + "/" + errorCode
271 + " " + completionState);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700272 runOnUiThread(() -> {
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700273 setUiEngineErrorCode(errorCode);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700274 });
275 }
276
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700277 /**
278 * Invoked when update progress changes.
279 */
280 private void onProgressUpdate(double progress) {
281 mProgressBar.setProgress((int) (100 * progress));
282 }
283
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700284 /** resets ui */
Zhomart Mukhamejanov469b35a2018-06-01 12:41:20 -0700285 private void uiResetWidgets() {
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700286 mTextViewBuild.setText(Build.DISPLAY);
Zhomart Mukhamejanov469b35a2018-06-01 12:41:20 -0700287 mSpinnerConfigs.setEnabled(false);
288 mButtonReload.setEnabled(false);
289 mButtonApplyConfig.setEnabled(false);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700290 mButtonStop.setEnabled(false);
291 mButtonReset.setEnabled(false);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700292 mProgressBar.setEnabled(false);
293 mProgressBar.setVisibility(ProgressBar.INVISIBLE);
Zhomart Mukhamejanov469b35a2018-06-01 12:41:20 -0700294 mButtonSwitchSlot.setEnabled(false);
295 mTextViewUpdateInfo.setTextColor(Color.parseColor("#aaaaaa"));
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700296 }
297
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700298 private void uiResetEngineText() {
299 mTextViewEngineStatus.setText(R.string.unknown);
300 mTextViewEngineErrorCode.setText(R.string.unknown);
Zhomart Mukhamejanov469b35a2018-06-01 12:41:20 -0700301 // Note: Do not reset mTextViewUpdaterState; UpdateManager notifies updater state properly.
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700302 }
303
Zhomart Mukhamejanov469b35a2018-06-01 12:41:20 -0700304 private void uiStateIdle() {
305 uiResetWidgets();
306 mSpinnerConfigs.setEnabled(true);
307 mButtonReload.setEnabled(true);
308 mButtonApplyConfig.setEnabled(true);
309 mProgressBar.setProgress(0);
310 }
311
312 private void uiStateRunning() {
313 uiResetWidgets();
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700314 mProgressBar.setEnabled(true);
Zhomart Mukhamejanov469b35a2018-06-01 12:41:20 -0700315 mProgressBar.setVisibility(ProgressBar.VISIBLE);
316 mButtonStop.setEnabled(true);
317 }
318
319 private void uiStatePaused() {
320 uiResetWidgets();
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700321 mButtonReset.setEnabled(true);
Zhomart Mukhamejanov469b35a2018-06-01 12:41:20 -0700322 mProgressBar.setEnabled(true);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700323 mProgressBar.setVisibility(ProgressBar.VISIBLE);
324 }
325
Zhomart Mukhamejanov469b35a2018-06-01 12:41:20 -0700326 private void uiStateSlotSwitchRequired() {
327 uiResetWidgets();
328 mButtonReset.setEnabled(true);
329 mProgressBar.setEnabled(true);
330 mProgressBar.setVisibility(ProgressBar.VISIBLE);
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700331 mButtonSwitchSlot.setEnabled(true);
332 mTextViewUpdateInfo.setTextColor(Color.parseColor("#777777"));
333 }
334
Zhomart Mukhamejanov469b35a2018-06-01 12:41:20 -0700335 private void uiStateError() {
336 uiResetWidgets();
337 mButtonReset.setEnabled(true);
338 mProgressBar.setEnabled(true);
339 mProgressBar.setVisibility(ProgressBar.VISIBLE);
340 }
341
342 private void uiStateRebootRequired() {
343 uiResetWidgets();
344 mButtonReset.setEnabled(true);
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700345 }
346
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700347 /**
348 * loads json configurations from configs dir that is defined in {@link UpdateConfigs}.
349 */
350 private void loadUpdateConfigs() {
351 mConfigs = UpdateConfigs.getUpdateConfigs(this);
352 loadConfigsToSpinner(mConfigs);
353 }
354
355 /**
356 * @param status update engine status code
357 */
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700358 private void setUiEngineStatus(int status) {
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700359 String statusText = UpdateEngineStatuses.getStatusText(status);
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700360 mTextViewEngineStatus.setText(statusText + "/" + status);
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700361 }
362
363 /**
364 * @param errorCode update engine error code
365 */
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700366 private void setUiEngineErrorCode(int errorCode) {
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700367 String errorText = UpdateEngineErrorCodes.getCodeName(errorCode);
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700368 mTextViewEngineErrorCode.setText(errorText + "/" + errorCode);
369 }
370
371 /**
372 * @param state updater sample state
373 */
374 private void setUiUpdaterState(int state) {
Zhomart Mukhamejanov674aa6c2018-05-25 17:00:11 -0700375 String stateText = UpdaterState.getStateText(state);
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700376 mTextViewUpdaterState.setText(stateText + "/" + state);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700377 }
378
379 private void loadConfigsToSpinner(List<UpdateConfig> configs) {
380 String[] spinnerArray = UpdateConfigs.configsToNames(configs);
381 ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(this,
382 android.R.layout.simple_spinner_item,
383 spinnerArray);
384 spinnerArrayAdapter.setDropDownViewResource(android.R.layout
385 .simple_spinner_dropdown_item);
386 mSpinnerConfigs.setAdapter(spinnerArrayAdapter);
387 }
388
389 private UpdateConfig getSelectedConfig() {
390 return mConfigs.get(mSpinnerConfigs.getSelectedItemPosition());
391 }
392
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700393}