| /* |
| * Copyright (C) 2021 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.managedprovisioning.common; |
| |
| import static java.util.Objects.requireNonNull; |
| |
| import android.app.Activity; |
| import android.content.Intent; |
| import android.os.Bundle; |
| import android.os.Parcelable; |
| |
| import androidx.annotation.Nullable; |
| import androidx.lifecycle.ViewModelProvider; |
| |
| import com.android.managedprovisioning.R; |
| import com.android.managedprovisioning.common.RetryLaunchViewModel.Config; |
| import com.android.managedprovisioning.common.RetryLaunchViewModel.LaunchActivityEvent; |
| import com.android.managedprovisioning.common.RetryLaunchViewModel.RetryLaunchViewModelFactory; |
| |
| /** |
| * An {@link Activity} which tries to start the {@link #EXTRA_INTENT_TO_LAUNCH} intent |
| * {@link #EXTRA_MAX_RETRIES} times every {@link #EXTRA_RETRY_PERIOD_MS} milliseconds. |
| * |
| * <p>This {@link Activity} is meant to be used in cases where there is a possibility the {@link |
| * #EXTRA_INTENT_TO_LAUNCH} intent may not be available the first time, for example if the app |
| * that resolves the {@link Intent} is getting updated. |
| * |
| * <p>This {@link Activity} forwards the result code of the {@link Activity} that was resolved from |
| * the {@link #EXTRA_INTENT_TO_LAUNCH}. Upon failure to launch the {@link Intent}, this {@link |
| * Activity} returns {@link #RESULT_CANCELED}. |
| */ |
| public final class RetryLaunchActivity extends SetupGlifLayoutActivity { |
| |
| /** |
| * A {@link Parcelable} extra describing the {@link Intent} to be launched. |
| * |
| * <p>This is a required extra. Not supplying it will throw an {@link IllegalStateException}. |
| */ |
| public static final String EXTRA_INTENT_TO_LAUNCH = |
| "com.android.managedprovisioning.extra.INTENT_TO_LAUNCH"; |
| |
| /** |
| * An {@code int} extra which determines how many times to retry the activity relaunch. |
| * |
| * <p>Must be a non-negative number. |
| * |
| * <p>Default value is {@code 3}. |
| */ |
| public static final String EXTRA_MAX_RETRIES = |
| "com.android.managedprovisioning.extra.MAX_RETRIES"; |
| |
| /** |
| * A {@code long} extra which determines how long to wait between each retry, in milliseconds. |
| * |
| * <p>Must be a non-negative number. |
| * |
| * <p>Default value is {@code 60000} (one minute). |
| */ |
| public static final String EXTRA_RETRY_PERIOD_MS = |
| "com.android.managedprovisioning.extra.RETRY_PERIOD_MS"; |
| |
| private static final int DEFAULT_MAX_RETRIES = 3; |
| |
| private static final long DEFAULT_RETRY_PERIOD_MS = 60_000L; |
| |
| private static final int LAUNCH_ACTIVITY_REQUEST_CODE = 1; |
| |
| private RetryLaunchViewModel mViewModel; |
| |
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| if (!getIntent().hasExtra(EXTRA_INTENT_TO_LAUNCH)) { |
| throw new IllegalStateException("EXTRA_INTENT must be supplied."); |
| } |
| Intent activityIntent = getIntent().getParcelableExtra(EXTRA_INTENT_TO_LAUNCH); |
| requireNonNull(activityIntent); |
| |
| Config config = createConfigFromIntent(getIntent()); |
| mViewModel = createViewModelWithIntent(activityIntent, config); |
| setupViewModelObservation(); |
| |
| if (savedInstanceState == null) { |
| mViewModel.tryStartActivity(); |
| } |
| } |
| |
| private Config createConfigFromIntent(Intent intent) { |
| final int maxRetries = getMaxRetries(intent); |
| final long retryPeriodMs = getRetryPeriodMs(intent); |
| return new Config() { |
| @Override |
| public long getLaunchActivityRetryMillis() { |
| return retryPeriodMs; |
| } |
| |
| @Override |
| public int getLaunchActivityMaxRetries() { |
| return maxRetries; |
| } |
| }; |
| } |
| |
| private int getMaxRetries(Intent intent) { |
| int maxRetries = intent.getIntExtra(EXTRA_MAX_RETRIES, DEFAULT_MAX_RETRIES); |
| if (maxRetries < 0) { |
| ProvisionLogger.loge("Invalid value passed for " + EXTRA_MAX_RETRIES + ". Expected a " |
| + "non-negative value but got " + maxRetries); |
| maxRetries = DEFAULT_MAX_RETRIES; |
| } |
| return maxRetries; |
| } |
| |
| private long getRetryPeriodMs(Intent intent) { |
| long retryPeriodMs = intent.getLongExtra(EXTRA_RETRY_PERIOD_MS, DEFAULT_RETRY_PERIOD_MS); |
| if (retryPeriodMs < 0) { |
| ProvisionLogger.loge("Invalid value passed for " + EXTRA_RETRY_PERIOD_MS + ". Expected " |
| + "a non-negative value but got " + retryPeriodMs); |
| retryPeriodMs = DEFAULT_RETRY_PERIOD_MS; |
| } |
| return retryPeriodMs; |
| } |
| |
| private void setupViewModelObservation() { |
| mViewModel.observeViewModelEvents().observe(this, viewModelEvent -> { |
| switch (viewModelEvent.getType()) { |
| case RetryLaunchViewModel.VIEW_MODEL_EVENT_LAUNCH_UPDATER: |
| launchActivity(((LaunchActivityEvent) viewModelEvent).getIntent()); |
| break; |
| case RetryLaunchViewModel.VIEW_MODEL_EVENT_WAITING_FOR_RETRY: |
| initializeUi(); |
| break; |
| case RetryLaunchViewModel.VIEW_MODEL_EVENT_LAUNCH_FAILURE: |
| finishWithCancelResult(); |
| break; |
| } |
| }); |
| } |
| |
| private RetryLaunchViewModel createViewModelWithIntent(Intent activityIntent, Config config) { |
| return new ViewModelProvider(this, |
| new RetryLaunchViewModelFactory( |
| getApplication(), |
| activityIntent, |
| config, |
| mUtils)) |
| .get(RetryLaunchViewModel.class); |
| } |
| |
| @Override |
| protected void onStop() { |
| super.onStop(); |
| mViewModel.stopLaunchRetries(); |
| } |
| |
| @Override |
| protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { |
| if (requestCode != LAUNCH_ACTIVITY_REQUEST_CODE) { |
| super.onActivityResult(requestCode, resultCode, data); |
| return; |
| } |
| ProvisionLogger.logi("Retry launcher activity result code: " + resultCode); |
| finishWithResult(resultCode); |
| } |
| |
| private void initializeUi() { |
| // TODO(b/208822780): Decide strings for this screen |
| int headerResId = R.string.downloading_administrator_header; |
| int titleResId = R.string.setup_device_progress; |
| initializeLayoutParams(R.layout.empty_loading_layout, headerResId); |
| setTitle(titleResId); |
| } |
| |
| private void launchActivity(Intent intent) { |
| getTransitionHelper().startActivityForResultWithTransition( |
| this, |
| intent, |
| LAUNCH_ACTIVITY_REQUEST_CODE); |
| } |
| |
| private void finishWithResult(int resultCode) { |
| setResult(resultCode); |
| getTransitionHelper().finishActivity(this); |
| } |
| |
| private void finishWithCancelResult() { |
| finishWithResult(RESULT_CANCELED); |
| } |
| } |