blob: d27a5f6c932114db31c580220558a578b4a7cab1 [file] [log] [blame]
/*
* Copyright (C) 2016 The CyanogenMod Project
* Copyright (C) 2017-2021 The LineageOS 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 org.lineageos.setupwizard;
import static android.view.View.INVISIBLE;
import static com.google.android.setupcompat.util.ResultCodes.RESULT_ACTIVITY_NOT_FOUND;
import static com.google.android.setupcompat.util.ResultCodes.RESULT_RETRY;
import static com.google.android.setupcompat.util.ResultCodes.RESULT_SKIP;
import static org.lineageos.setupwizard.SetupWizardApp.ACTION_EMERGENCY_DIAL;
import static org.lineageos.setupwizard.SetupWizardApp.ACTION_NEXT;
import static org.lineageos.setupwizard.SetupWizardApp.ACTION_SETUP_COMPLETE;
import static org.lineageos.setupwizard.SetupWizardApp.EXTRA_ACTION_ID;
import static org.lineageos.setupwizard.SetupWizardApp.EXTRA_HAS_MULTIPLE_USERS;
import static org.lineageos.setupwizard.SetupWizardApp.EXTRA_RESULT_CODE;
import static org.lineageos.setupwizard.SetupWizardApp.EXTRA_SCRIPT_URI;
import static org.lineageos.setupwizard.SetupWizardApp.LOGV;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import com.android.settingslib.Utils;
import com.google.android.setupcompat.util.WizardManagerHelper;
import com.google.android.setupdesign.GlifLayout;
import org.lineageos.setupwizard.NavigationLayout.NavigationBarListener;
import org.lineageos.setupwizard.util.SetupWizardUtils;
import java.util.List;
public abstract class BaseSetupWizardActivity extends Activity implements NavigationBarListener,
ViewTreeObserver.OnPreDrawListener {
public static final String TAG = BaseSetupWizardActivity.class.getSimpleName();
protected static final int TRANSITION_ID_NONE = -1;
protected static final int TRANSITION_ID_DEFAULT = 1;
protected static final int TRANSITION_ID_SLIDE = 2;
protected static final int TRANSITION_ID_FADE = 3;
protected static final int NEXT_REQUEST = 10000;
protected static final int EMERGENCY_DIAL_ACTIVITY_REQUEST = 10038;
protected static final int WIFI_ACTIVITY_REQUEST = 10004;
protected static final int BLUETOOTH_ACTIVITY_REQUEST = 10100;
protected static final int BIOMETRIC_ACTIVITY_REQUEST = 10101;
protected static final int SCREENLOCK_ACTIVITY_REQUEST = 10102;
private static final int IMMERSIVE_FLAGS = View.STATUS_BAR_DISABLE_HOME
| View.STATUS_BAR_DISABLE_RECENT;
private int mSystemUiFlags = IMMERSIVE_FLAGS;
private NavigationLayout mNavigationBar;
protected boolean mIsActivityVisible = false;
protected boolean mIsExiting = false;
private boolean mIsFirstRun = true;
protected boolean mIsGoingBack = false;
private boolean mIsPrimaryUser;
protected int mResultCode = 0;
private Intent mResultData;
private final BroadcastReceiver finishReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (ACTION_SETUP_COMPLETE.equals(intent.getAction())) {
if (BaseSetupWizardActivity.this instanceof FinishActivity) return;
if (mNavigationBar != null) {
// hide the activity's view, so it does not pop up again
mNavigationBar.getRootView().setVisibility(INVISIBLE);
}
}
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
if (LOGV) {
logActivityState("onCreate savedInstanceState=" + savedInstanceState);
}
super.onCreate(savedInstanceState);
registerReceiver(finishReceiver, new IntentFilter(ACTION_SETUP_COMPLETE));
mIsPrimaryUser = UserHandle.myUserId() == 0;
initLayout();
mNavigationBar = getNavigationBar();
if (mNavigationBar != null) {
mNavigationBar.setNavigationBarListener(this);
mNavigationBar.setSystemUiVisibility(mSystemUiFlags);
// Set the UI flags before draw because the visibility might change in unexpected /
// undetectable times, like transitioning from a finishing activity that had a keyboard
ViewTreeObserver viewTreeObserver = mNavigationBar.getViewTreeObserver();
viewTreeObserver.addOnPreDrawListener(this);
}
}
@Override
protected void onStart() {
if (LOGV) {
logActivityState("onStart");
}
super.onStart();
exitIfSetupComplete();
}
@Override
protected void onRestart() {
if (LOGV) {
logActivityState("onRestart");
}
super.onRestart();
}
@Override
protected void onResume() {
if (LOGV) {
logActivityState("onResume");
}
super.onResume();
if (mIsGoingBack) {
if (!mIsExiting) {
applyBackwardTransition(getTransition());
}
} else if (!mIsExiting) {
applyForwardTransition(getTransition());
}
}
@Override
protected void onPause() {
if (LOGV) {
logActivityState("onPause");
}
super.onPause();
}
@Override
protected void onStop() {
if (LOGV) {
logActivityState("onStop");
}
super.onStop();
}
@Override
protected void onDestroy() {
if (LOGV) {
logActivityState("onDestroy");
}
unregisterReceiver(finishReceiver);
super.onDestroy();
}
@Override
public void onAttachedToWindow() {
if (LOGV) {
logActivityState("onAttachedToWindow");
}
mIsActivityVisible = true;
super.onAttachedToWindow();
}
@Override
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
if (LOGV) {
Log.v(TAG, "onRestoreInstanceState(" + savedInstanceState + ")");
}
super.onRestoreInstanceState(savedInstanceState);
int currentId = savedInstanceState.getInt("currentFocus", -1);
if (currentId != -1) {
View view = findViewById(currentId);
if (view != null) {
view.requestFocus();
}
}
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
View current = getCurrentFocus();
outState.putInt("currentFocus", current != null ? current.getId() : -1);
if (LOGV) {
Log.v(TAG, "onSaveInstanceState(" + outState + ")");
}
}
@Override
public boolean onPreDraw() {
// View.setSystemUiVisibility checks if the visibility changes before applying them
// so the performance impact is contained
mNavigationBar.setSystemUiVisibility(mSystemUiFlags);
return true;
}
/**
* @return The navigation bar instance in the layout, or null if the layout does not have a
* navigation bar.
*/
public NavigationLayout getNavigationBar() {
final View view = findViewById(R.id.navigation_bar);
return view instanceof NavigationLayout ? (NavigationLayout) view : null;
}
public void setNextAllowed(boolean allowed) {
if (mNavigationBar != null) {
mNavigationBar.getNextButton().setEnabled(allowed);
}
}
protected boolean isNextAllowed() {
if (mNavigationBar != null) {
mNavigationBar.getNextButton().isEnabled();
}
return false;
}
protected void onNextPressed() {
nextAction(NEXT_REQUEST);
}
protected void onSkipPressed() {
nextAction(NEXT_REQUEST);
}
protected void setNextText(int resId) {
if (mNavigationBar != null) {
mNavigationBar.getNextButton().setText(resId);
}
}
protected void setSkipText(int resId) {
if (mNavigationBar != null) {
mNavigationBar.getSkipButton().setText(resId);
}
}
protected void hideNextButton() {
if (mNavigationBar != null) {
Animation fadeOut = AnimationUtils.loadAnimation(this, android.R.anim.fade_out);
final Button next = mNavigationBar.getNextButton();
next.startAnimation(fadeOut);
next.setVisibility(INVISIBLE);
}
}
protected Intent getResultData() {
return null;
}
@Override
public void onBackPressed() {
if (LOGV) {
Log.v(TAG, "onBackPressed()");
}
setResultCode(RESULT_CANCELED, getResultData());
super.onBackPressed();
}
public void onNavigateBack() {
onBackPressed();
}
public void onNavigateNext() {
onNextPressed();
}
public void onSkip() {
onSkipPressed();
}
protected void startEmergencyDialer() {
try {
startFirstRunActivityForResult(new Intent(ACTION_EMERGENCY_DIAL),
EMERGENCY_DIAL_ACTIVITY_REQUEST);
applyForwardTransition(TRANSITION_ID_DEFAULT);
} catch (ActivityNotFoundException e) {
Log.e(TAG, "Can't find the emergency dialer: com.android.phone.EmergencyDialer.DIAL");
}
}
protected void onSetupStart() {
SetupWizardUtils.disableCaptivePortalDetection(getApplicationContext());
SetupWizardUtils.disableStatusBar(getApplicationContext());
tryEnablingWifi();
}
protected void exitIfSetupComplete() {
if (WizardManagerHelper.isUserSetupComplete(this)) {
Log.i(TAG, "Starting activity with USER_SETUP_COMPLETE=true");
startSetupWizardExitActivity();
setResult(RESULT_CANCELED, null);
finishAllAppTasks();
}
}
protected void finishAllAppTasks() {
List<ActivityManager.AppTask> appTasks =
getSystemService(ActivityManager.class).getAppTasks();
for (ActivityManager.AppTask task : appTasks) {
if (LOGV) {
Log.v(TAG, "Finishing task=" + task.toString());
}
task.finishAndRemoveTask();
}
finish();
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (LOGV) {
Log.v(TAG, "onActivityResult(" + getRequestName(requestCode) + ", " +
getResultName(requestCode, resultCode) + ")");
}
mIsGoingBack = true;
if (requestCode != NEXT_REQUEST || resultCode != RESULT_CANCELED) {
if (requestCode == EMERGENCY_DIAL_ACTIVITY_REQUEST) {
applyBackwardTransition(TRANSITION_ID_DEFAULT);
return;
}
if (resultCode == RESULT_CANCELED) {
finish();
} else {
nextAction(resultCode);
}
}
}
public void finish() {
if (LOGV) {
Log.v(TAG, "finish");
}
super.finish();
if (isResumed() && mResultCode == RESULT_CANCELED) {
applyBackwardTransition(getTransition());
}
mIsExiting = true;
}
protected void finishAction() {
finishAction(RESULT_CANCELED);
}
protected void finishAction(int resultCode) {
finishAction(resultCode, null);
}
protected void finishAction(int resultCode, Intent data) {
if (resultCode != 0) {
nextAction(resultCode, data);
}
finish();
}
protected void setResultCode(int resultCode) {
setResultCode(resultCode, getResultData());
}
protected void setResultCode(int resultCode, Intent data) {
if (LOGV) {
Log.v(TAG, "setResultCode result=" + getResultName(0, resultCode) + " data=" + data);
}
mResultCode = resultCode;
mResultData = data;
setResult(resultCode, data);
}
protected void nextAction(int resultCode) {
nextAction(resultCode, null);
}
protected void nextAction(int resultCode, Intent data) {
if (LOGV) {
Log.v(TAG, "nextAction resultCode=" + resultCode +
" data=" + data + " this=" + this);
}
if (resultCode == 0) {
throw new IllegalArgumentException("Cannot call nextAction with RESULT_CANCELED");
}
setResultCode(resultCode, data);
sendActionResults();
}
public void startActivity(Intent intent) {
super.startActivity(intent);
if (isResumed() && mIsActivityVisible) {
applyForwardTransition(getTransition());
}
mIsExiting = true;
}
public void startActivityForResult(Intent intent, int requestCode) {
super.startActivityForResult(intent, requestCode);
if (isResumed() && mIsActivityVisible) {
applyForwardTransition(getTransition());
}
mIsExiting = true;
}
protected void sendActionResults() {
if (LOGV) {
Log.v(TAG, "sendActionResults resultCode=" + mResultCode + " data=" + mResultData);
}
Intent intent = new Intent(ACTION_NEXT);
intent.putExtra(EXTRA_SCRIPT_URI, getIntent().getStringExtra(EXTRA_SCRIPT_URI));
intent.putExtra(EXTRA_ACTION_ID, getIntent().getStringExtra(EXTRA_ACTION_ID));
intent.putExtra(EXTRA_RESULT_CODE, mResultCode);
if (!(mResultData == null || mResultData.getExtras() == null)) {
intent.putExtras(mResultData.getExtras());
}
startActivityForResult(intent, NEXT_REQUEST);
}
protected void applyForwardTransition(int transitionId) {
if (transitionId == TRANSITION_ID_SLIDE) {
overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
} else if (transitionId == TRANSITION_ID_FADE) {
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
} else if (transitionId == TRANSITION_ID_DEFAULT) {
TypedArray typedArray = obtainStyledAttributes(android.R.style.Animation_Activity,
new int[]{android.R.attr.activityOpenEnterAnimation,
android.R.attr.activityOpenExitAnimation});
overridePendingTransition(typedArray.getResourceId(0, 0),
typedArray.getResourceId(1, 0));
typedArray.recycle();
} else if (transitionId == TRANSITION_ID_NONE) {
overridePendingTransition(0, 0);
}
}
protected void applyBackwardTransition(int transitionId) {
if (transitionId == TRANSITION_ID_SLIDE) {
overridePendingTransition(R.anim.sud_slide_back_in, R.anim.sud_slide_back_out);
} else if (transitionId == TRANSITION_ID_FADE) {
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
} else if (transitionId == TRANSITION_ID_DEFAULT) {
TypedArray typedArray = obtainStyledAttributes(android.R.style.Animation_Activity,
new int[]{android.R.attr.activityCloseEnterAnimation,
android.R.attr.activityCloseExitAnimation});
overridePendingTransition(typedArray.getResourceId(0, 0),
typedArray.getResourceId(1, 0));
typedArray.recycle();
} else if (transitionId == TRANSITION_ID_NONE) {
overridePendingTransition(0, 0);
}
}
protected int getTransition() {
return TRANSITION_ID_SLIDE;
}
protected boolean tryEnablingWifi() {
WifiManager wifiManager = getSystemService(WifiManager.class);
if (wifiManager != null && !wifiManager.isWifiEnabled()) {
return wifiManager.setWifiEnabled(true);
}
return false;
}
private void startSetupWizardExitActivity() {
if (LOGV) {
Log.v(TAG, "startSetupWizardExitActivity()");
}
startFirstRunActivity(new Intent(this, SetupWizardExitActivity.class));
}
protected void startFirstRunActivity(Intent intent) {
if (LOGV) {
Log.v(TAG, "starting activity " + intent);
}
intent.putExtra(WizardManagerHelper.EXTRA_IS_FIRST_RUN, isFirstRun());
intent.putExtra(EXTRA_HAS_MULTIPLE_USERS, hasMultipleUsers());
startActivity(intent);
}
protected void startFirstRunActivityForResult(Intent intent, int requestCode) {
if (LOGV) {
Log.v(TAG, "startFirstRunActivityForResult requestCode=" + requestCode);
}
intent.putExtra(WizardManagerHelper.EXTRA_IS_FIRST_RUN, isFirstRun());
intent.putExtra(EXTRA_HAS_MULTIPLE_USERS, hasMultipleUsers());
startActivityForResult(intent, requestCode);
}
protected boolean isFirstRun() {
return mIsFirstRun;
}
protected boolean isPrimaryUser() {
return mIsPrimaryUser;
}
public boolean hasMultipleUsers() {
return ((UserManager) getSystemService(USER_SERVICE)).getUsers().size() > 1;
}
protected void logActivityState(String prefix) {
Log.v(TAG, prefix + " isResumed=" + isResumed() + " isFinishing=" +
isFinishing() + " isDestroyed=" + isDestroyed());
}
protected static String getRequestName(int requestCode) {
StringBuilder sb = new StringBuilder();
switch (requestCode) {
case NEXT_REQUEST:
sb.append("NEXT_REQUEST");
break;
case EMERGENCY_DIAL_ACTIVITY_REQUEST:
sb.append("EMERGENCY_DIAL_ACTIVITY_REQUEST");
break;
case WIFI_ACTIVITY_REQUEST:
sb.append("WIFI_ACTIVITY_REQUEST");
break;
case BLUETOOTH_ACTIVITY_REQUEST:
sb.append("BLUETOOTH_ACTIVITY_REQUEST");
break;
case BIOMETRIC_ACTIVITY_REQUEST:
sb.append("BIOMETRIC_ACTIVITY_REQUEST");
break;
case SCREENLOCK_ACTIVITY_REQUEST:
sb.append("SCREENLOCK_ACTIVITY_REQUEST");
break;
}
sb.append("(").append(requestCode).append(")");
return sb.toString();
}
protected static String getResultName(int requestCode, int resultCode) {
StringBuilder sb = new StringBuilder();
switch (requestCode) {
case WIFI_ACTIVITY_REQUEST:
switch (resultCode) {
case RESULT_OK:
sb.append("RESULT_OK");
break;
case RESULT_CANCELED:
sb.append("RESULT_CANCELED");
break;
case RESULT_SKIP:
sb.append("RESULT_WIFI_SKIP");
break;
default:
break;
}
case BLUETOOTH_ACTIVITY_REQUEST:
switch (resultCode) {
case RESULT_OK:
sb.append("RESULT_OK");
break;
case RESULT_CANCELED:
sb.append("RESULT_CANCELED");
break;
case RESULT_SKIP:
sb.append("RESULT_BLUETOOTH_SKIP");
break;
default:
break;
}
case BIOMETRIC_ACTIVITY_REQUEST:
switch (resultCode) {
case RESULT_OK:
sb.append("RESULT_OK");
break;
case RESULT_CANCELED:
sb.append("RESULT_CANCELED");
break;
case RESULT_SKIP:
sb.append("RESULT_BIOMETRIC_SKIP");
break;
default:
break;
}
case SCREENLOCK_ACTIVITY_REQUEST:
switch (resultCode) {
case RESULT_OK:
sb.append("RESULT_OK");
break;
case RESULT_CANCELED:
sb.append("RESULT_CANCELED");
break;
case RESULT_SKIP:
sb.append("RESULT_SCREENLOCK_SKIP");
break;
default:
break;
}
default:
switch (resultCode) {
case RESULT_OK:
sb.append("RESULT_OK");
break;
case RESULT_CANCELED:
sb.append("RESULT_CANCELED");
break;
case RESULT_SKIP:
sb.append("RESULT_SKIP");
break;
case RESULT_RETRY:
sb.append("RESULT_RETRY");
break;
case RESULT_ACTIVITY_NOT_FOUND:
sb.append("RESULT_ACTIVITY_NOT_FOUND");
break;
}
break;
}
sb.append("(").append(resultCode).append(")");
return sb.toString();
}
private void initLayout() {
if (getLayoutResId() != -1) {
setContentView(getLayoutResId());
}
if (getTitleResId() != -1) {
final CharSequence headerText = TextUtils.expandTemplate(getText(getTitleResId()));
getGlifLayout().setHeaderText(headerText);
}
if (getIconResId() != -1) {
final GlifLayout layout = getGlifLayout();
final Drawable icon = getDrawable(getIconResId()).mutate();
icon.setTintList(Utils.getColorAccent(layout.getContext()));
layout.setIcon(icon);
}
}
protected GlifLayout getGlifLayout() {
return requireViewById(R.id.setup_wizard_layout);
}
protected int getLayoutResId() {
return -1;
}
protected int getTitleResId() {
return -1;
}
protected int getIconResId() {
return -1;
}
}