blob: 1de72c2d6d948c803cb64017a907b6dcc76f6409 [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
91 uiReset();
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 // TODO(zhomart) load saved states
112 // Binding to UpdateEngine invokes onStatusUpdate callback,
113 // persisted updater state has to be loaded and prepared beforehand.
Zhomart Mukhamejanov6f26e712018-05-18 10:15:31 -0700114 this.mUpdateManager.bind();
115 }
116
117 @Override
118 protected void onPause() {
119 this.mUpdateManager.unbind();
Zhomart Mukhamejanov7671f682018-05-24 09:11:47 -0700120 // TODO(zhomart) save state
Zhomart Mukhamejanov6f26e712018-05-18 10:15:31 -0700121 super.onPause();
122 }
123
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700124 /**
125 * reload button is clicked
126 */
127 public void onReloadClick(View view) {
128 loadUpdateConfigs();
129 }
130
131 /**
132 * view config button is clicked
133 */
134 public void onViewConfigClick(View view) {
135 UpdateConfig config = mConfigs.get(mSpinnerConfigs.getSelectedItemPosition());
136 new AlertDialog.Builder(this)
137 .setTitle(config.getName())
138 .setMessage(config.getRawJson())
139 .setPositiveButton(R.string.close, (dialog, id) -> dialog.dismiss())
140 .show();
141 }
142
143 /**
144 * apply config button is clicked
145 */
146 public void onApplyConfigClick(View view) {
147 new AlertDialog.Builder(this)
148 .setTitle("Apply Update")
149 .setMessage("Do you really want to apply this update?")
150 .setIcon(android.R.drawable.ic_dialog_alert)
151 .setPositiveButton(android.R.string.ok, (dialog, whichButton) -> {
152 uiSetUpdating();
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700153 uiResetEngineText();
Zhomart Mukhamejanov6f26e712018-05-18 10:15:31 -0700154 mUpdateManager.applyUpdate(this, getSelectedConfig());
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700155 })
156 .setNegativeButton(android.R.string.cancel, null)
157 .show();
158 }
159
160 /**
161 * stop button clicked
162 */
163 public void onStopClick(View view) {
164 new AlertDialog.Builder(this)
165 .setTitle("Stop Update")
166 .setMessage("Do you really want to cancel running update?")
167 .setIcon(android.R.drawable.ic_dialog_alert)
168 .setPositiveButton(android.R.string.ok, (dialog, whichButton) -> {
Zhomart Mukhamejanov6f26e712018-05-18 10:15:31 -0700169 mUpdateManager.cancelRunningUpdate();
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700170 })
171 .setNegativeButton(android.R.string.cancel, null).show();
172 }
173
174 /**
175 * reset button clicked
176 */
177 public void onResetClick(View view) {
178 new AlertDialog.Builder(this)
179 .setTitle("Reset Update")
180 .setMessage("Do you really want to cancel running update"
181 + " and restore old version?")
182 .setIcon(android.R.drawable.ic_dialog_alert)
183 .setPositiveButton(android.R.string.ok, (dialog, whichButton) -> {
Zhomart Mukhamejanov6f26e712018-05-18 10:15:31 -0700184 mUpdateManager.resetUpdate();
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700185 })
186 .setNegativeButton(android.R.string.cancel, null).show();
187 }
188
189 /**
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700190 * switch slot button clicked
191 */
192 public void onSwitchSlotClick(View view) {
Zhomart Mukhamejanov6f26e712018-05-18 10:15:31 -0700193 mUpdateManager.setSwitchSlotOnReboot();
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700194 }
195
196 /**
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700197 * Invoked when SystemUpdaterSample app state changes.
198 * Value of {@code state} will be one of the
Zhomart Mukhamejanov674aa6c2018-05-25 17:00:11 -0700199 * values from {@link UpdaterState}.
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700200 */
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700201 private void onUpdaterStateChange(int state) {
202 Log.i(TAG, "onUpdaterStateChange invoked state=" + state);
Zhomart Mukhamejanov6f26e712018-05-18 10:15:31 -0700203 runOnUiThread(() -> {
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700204 setUiUpdaterState(state);
205 });
206 }
207
208 /**
209 * Invoked when {@link UpdateEngine} status changes. Value of {@code status} will
210 * be one of the values from {@link UpdateEngine.UpdateStatusConstants}.
211 */
212 private void onEngineStatusUpdate(int status) {
213 runOnUiThread(() -> {
214 Log.e(TAG, "StatusUpdate - status="
Zhomart Mukhamejanov6f26e712018-05-18 10:15:31 -0700215 + UpdateEngineStatuses.getStatusText(status)
216 + "/" + status);
Zhomart Mukhamejanov6f26e712018-05-18 10:15:31 -0700217 if (status == UpdateEngine.UpdateStatusConstants.IDLE) {
218 Log.d(TAG, "status changed, resetting ui");
219 uiReset();
220 } else {
221 Log.d(TAG, "status changed, setting ui to updating mode");
222 uiSetUpdating();
223 }
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700224 setUiEngineStatus(status);
Zhomart Mukhamejanov6f26e712018-05-18 10:15:31 -0700225 });
226 }
227
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700228 /**
229 * Invoked when the payload has been applied, whether successfully or
230 * unsuccessfully. The value of {@code errorCode} will be one of the
231 * values from {@link UpdateEngine.ErrorCodeConstants}.
232 */
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700233 private void onEnginePayloadApplicationComplete(int errorCode) {
234 final String completionState = UpdateEngineErrorCodes.isUpdateSucceeded(errorCode)
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700235 ? "SUCCESS"
236 : "FAILURE";
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700237 runOnUiThread(() -> {
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700238 Log.i(TAG,
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700239 "Completed - errorCode="
Zhomart Mukhamejanov674aa6c2018-05-25 17:00:11 -0700240 + UpdateEngineErrorCodes.getCodeName(errorCode) + "/" + errorCode
241 + " " + completionState);
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700242 setUiEngineErrorCode(errorCode);
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700243 if (errorCode == UpdateEngineErrorCodes.UPDATED_BUT_NOT_ACTIVE) {
244 // if update was successfully applied.
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700245 if (mUpdateManager.isManualSwitchSlotRequired()) {
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700246 // Show "Switch Slot" button.
247 uiShowSwitchSlotInfo();
248 }
249 }
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700250 });
251 }
252
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700253 /**
254 * Invoked when update progress changes.
255 */
256 private void onProgressUpdate(double progress) {
257 mProgressBar.setProgress((int) (100 * progress));
258 }
259
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700260 /** resets ui */
261 private void uiReset() {
262 mTextViewBuild.setText(Build.DISPLAY);
263 mSpinnerConfigs.setEnabled(true);
264 mButtonReload.setEnabled(true);
265 mButtonApplyConfig.setEnabled(true);
266 mButtonStop.setEnabled(false);
267 mButtonReset.setEnabled(false);
268 mProgressBar.setProgress(0);
269 mProgressBar.setEnabled(false);
270 mProgressBar.setVisibility(ProgressBar.INVISIBLE);
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700271 uiHideSwitchSlotInfo();
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700272 }
273
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700274 private void uiResetEngineText() {
275 mTextViewEngineStatus.setText(R.string.unknown);
276 mTextViewEngineErrorCode.setText(R.string.unknown);
277 // Note: Do not reset mTextViewUpdaterState; UpdateManager notifies properly.
278 }
279
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700280 /** sets ui updating mode */
281 private void uiSetUpdating() {
282 mTextViewBuild.setText(Build.DISPLAY);
283 mSpinnerConfigs.setEnabled(false);
284 mButtonReload.setEnabled(false);
285 mButtonApplyConfig.setEnabled(false);
286 mButtonStop.setEnabled(true);
287 mProgressBar.setEnabled(true);
288 mButtonReset.setEnabled(true);
289 mProgressBar.setVisibility(ProgressBar.VISIBLE);
290 }
291
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700292 private void uiShowSwitchSlotInfo() {
293 mButtonSwitchSlot.setEnabled(true);
294 mTextViewUpdateInfo.setTextColor(Color.parseColor("#777777"));
295 }
296
297 private void uiHideSwitchSlotInfo() {
298 mTextViewUpdateInfo.setTextColor(Color.parseColor("#AAAAAA"));
299 mButtonSwitchSlot.setEnabled(false);
300 }
301
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700302 /**
303 * loads json configurations from configs dir that is defined in {@link UpdateConfigs}.
304 */
305 private void loadUpdateConfigs() {
306 mConfigs = UpdateConfigs.getUpdateConfigs(this);
307 loadConfigsToSpinner(mConfigs);
308 }
309
310 /**
311 * @param status update engine status code
312 */
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700313 private void setUiEngineStatus(int status) {
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700314 String statusText = UpdateEngineStatuses.getStatusText(status);
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700315 mTextViewEngineStatus.setText(statusText + "/" + status);
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700316 }
317
318 /**
319 * @param errorCode update engine error code
320 */
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700321 private void setUiEngineErrorCode(int errorCode) {
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700322 String errorText = UpdateEngineErrorCodes.getCodeName(errorCode);
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700323 mTextViewEngineErrorCode.setText(errorText + "/" + errorCode);
324 }
325
326 /**
327 * @param state updater sample state
328 */
329 private void setUiUpdaterState(int state) {
Zhomart Mukhamejanov674aa6c2018-05-25 17:00:11 -0700330 String stateText = UpdaterState.getStateText(state);
Zhomart Mukhamejanov8f4059d2018-05-18 10:15:31 -0700331 mTextViewUpdaterState.setText(stateText + "/" + state);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700332 }
333
334 private void loadConfigsToSpinner(List<UpdateConfig> configs) {
335 String[] spinnerArray = UpdateConfigs.configsToNames(configs);
336 ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(this,
337 android.R.layout.simple_spinner_item,
338 spinnerArray);
339 spinnerArrayAdapter.setDropDownViewResource(android.R.layout
340 .simple_spinner_dropdown_item);
341 mSpinnerConfigs.setAdapter(spinnerArrayAdapter);
342 }
343
344 private UpdateConfig getSelectedConfig() {
345 return mConfigs.get(mSpinnerConfigs.getSelectedItemPosition());
346 }
347
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700348}