diff options
Diffstat (limited to 'app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java')
-rw-r--r-- | app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java new file mode 100644 index 00000000..27ca6658 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java @@ -0,0 +1,328 @@ +package se.leap.bitmaskclient.providersetup.activities; + +import static android.view.View.GONE; +import static android.view.View.VISIBLE; +import static androidx.appcompat.app.ActionBar.DISPLAY_SHOW_CUSTOM; +import static se.leap.bitmaskclient.base.utils.PreferenceHelper.deleteProviderDetailsFromPreferences; +import static se.leap.bitmaskclient.providersetup.fragments.SetupFragmentFactory.CONFIGURE_PROVIDER_FRAGMENT; +import static se.leap.bitmaskclient.tor.TorStatusObservable.TorStatus.OFF; + +import android.Manifest; +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; +import android.net.VpnService; +import android.os.Build; +import android.os.Bundle; +import android.util.Log; +import android.view.Gravity; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; + +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.content.ContextCompat; +import androidx.core.content.res.ResourcesCompat; +import androidx.fragment.app.DialogFragment; +import androidx.fragment.app.FragmentTransaction; +import androidx.viewpager2.widget.ViewPager2; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.HashSet; + +import se.leap.bitmaskclient.BuildConfig; +import se.leap.bitmaskclient.R; +import se.leap.bitmaskclient.base.FragmentManagerEnhanced; +import se.leap.bitmaskclient.base.models.Provider; +import se.leap.bitmaskclient.base.models.ProviderObservable; +import se.leap.bitmaskclient.base.utils.ViewHelper; +import se.leap.bitmaskclient.base.views.ActionBarTitle; +import se.leap.bitmaskclient.databinding.ActivitySetupBinding; +import se.leap.bitmaskclient.providersetup.ProviderSetupFailedDialog; +import se.leap.bitmaskclient.providersetup.SetupViewPagerAdapter; +import se.leap.bitmaskclient.tor.TorServiceCommand; +import se.leap.bitmaskclient.tor.TorStatusObservable; + +public class SetupActivity extends AppCompatActivity implements SetupActivityCallback, ProviderSetupFailedDialog.DownloadFailedDialogInterface { + + public static final String EXTRA_PROVIDER = "EXTRA_PROVIDER"; + public static final String EXTRA_CURRENT_POSITION = "EXTRA_CURRENT_POSITION"; + private static final String TAG = SetupActivity.class.getSimpleName(); + ActivitySetupBinding binding; + Provider provider; + private int currentPosition = 0; + + private final HashSet<CancelCallback> cancelCallbacks = new HashSet<>(); + private FragmentManagerEnhanced fragmentManager; + SetupViewPagerAdapter adapter; + + @SuppressLint("ClickableViewAccessibility") + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (savedInstanceState != null) { + provider = savedInstanceState.getParcelable(EXTRA_PROVIDER); + currentPosition = savedInstanceState.getInt(EXTRA_CURRENT_POSITION); + } + + binding = ActivitySetupBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + fragmentManager = new FragmentManagerEnhanced(getSupportFragmentManager()); + ArrayList<View> indicatorViews = new ArrayList<>(); + + for (int i = 0; i < 4; i++) { + addIndicatorView(indicatorViews); + } + + Intent requestVpnPermission = VpnService.prepare(this); + if (requestVpnPermission != null) { + addIndicatorView(indicatorViews); + addIndicatorView(indicatorViews); + } + + boolean showNotificationPermissionFragments = false; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + if (ContextCompat.checkSelfPermission(getApplication(), Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { + showNotificationPermissionFragments = shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS); + if (showNotificationPermissionFragments) { + addIndicatorView(indicatorViews); + addIndicatorView(indicatorViews); + } + } + } + + adapter = new SetupViewPagerAdapter(getSupportFragmentManager(), getLifecycle(), requestVpnPermission, showNotificationPermissionFragments); + + binding.viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { + @Override + public void onPageSelected(int position) { + super.onPageSelected(position); + currentPosition = position; + for (int i = 0; i < indicatorViews.size(); i++) { + ((ViewGroup) indicatorViews.get(i)). + getChildAt(0). + setBackgroundColor(ContextCompat.getColor(SetupActivity.this, (i == position) ? R.color.colorPrimaryDark : R.color.colorDisabled)); + } + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + getSupportActionBar().setDisplayHomeAsUpEnabled(position == 0 && ProviderObservable.getInstance().getCurrentProvider().isConfigured()); + } + } + }); + binding.viewPager.setAdapter(adapter); + binding.viewPager.setUserInputEnabled(false); + binding.viewPager.setCurrentItem(currentPosition, false); + + binding.setupNextButton.setOnClickListener(v -> { + int currentPos = binding.viewPager.getCurrentItem(); + int newPos = currentPos + 1; + if (newPos >= binding.viewPager.getAdapter().getItemCount()) { + Toast.makeText(SetupActivity.this, "SetupFinished \\o/", Toast.LENGTH_LONG).show(); + return; + } + binding.viewPager.setCurrentItem(newPos); + }); + binding.setupCancelButton.setOnClickListener(v -> { + cancel(); + }); + setupActionBar(); + } + + @Override + protected void onSaveInstanceState(@NonNull Bundle outState) { + super.onSaveInstanceState(outState); + if (provider != null) { + outState.putParcelable(EXTRA_PROVIDER, provider); + outState.putInt(EXTRA_CURRENT_POSITION, currentPosition); + } + } + + private void cancel() { + binding.viewPager.setCurrentItem(0, false); + if (TorStatusObservable.getStatus() != OFF) { + Log.d(TAG, "SHUTDOWN - cancelSettingUpProvider"); + TorServiceCommand.stopTorServiceAsync(this); + } + provider = null; + for (CancelCallback cancelCallback : cancelCallbacks) { + cancelCallback.onCanceled(); + } + } + + private void addIndicatorView(ArrayList<View> indicatorViews) { + // FIXME: we have to work around a bug in our usage of CardView, that + // doesn't let us programmatically add new indicator views as needed. + // for some reason the cardBackgroundColor property is ignored if we add + // the card view dynamically + View v = binding.indicatorContainer.getChildAt(indicatorViews.size()); + if (v == null) { + throw new IllegalStateException("Too few indicator views in layout hard-coded"); + } + v.setVisibility(VISIBLE); + indicatorViews.add(v); + } + + private void setupActionBar() { + setSupportActionBar(binding.toolbar); + final ActionBar actionBar = getSupportActionBar(); + Context context = actionBar.getThemedContext(); + actionBar.setDisplayOptions(DISPLAY_SHOW_CUSTOM); + + ActionBarTitle actionBarTitle = new ActionBarTitle(context); + actionBarTitle.setTitleCaps(BuildConfig.actionbar_capitalize_title); + actionBarTitle.setTitle(getString(R.string.app_name)); + + final Drawable upArrow = ResourcesCompat.getDrawable(getResources(), R.drawable.ic_back, getTheme()); + actionBar.setHomeAsUpIndicator(upArrow); + + actionBar.setDisplayHomeAsUpEnabled(currentPosition == 0 && ProviderObservable.getInstance().getCurrentProvider().isConfigured()); + ViewHelper.setActivityBarColor(this, R.color.bg_setup_status_bar, R.color.bg_setup_action_bar, R.color.colorActionBarTitleFont); + @ColorInt int titleColor = ContextCompat.getColor(context, R.color.colorActionBarTitleFont); + actionBarTitle.setTitleTextColor(titleColor); + + actionBarTitle.setCentered(BuildConfig.actionbar_center_title); + actionBarTitle.setSingleBoldTitle(); + if (BuildConfig.actionbar_center_title) { + ActionBar.LayoutParams params = new ActionBar.LayoutParams( + ActionBar.LayoutParams.WRAP_CONTENT, + ActionBar.LayoutParams.MATCH_PARENT, + Gravity.CENTER); + actionBar.setCustomView(actionBarTitle, params); + } else { + actionBar.setCustomView(actionBarTitle); + } + } + + @Override + public boolean onOptionsItemSelected(@NonNull MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + finish(); + return true; + } + return super.onOptionsItemSelected(item); + } + + @Override + public void onSetupStepValidationChanged(boolean isValid) { + binding.setupNextButton.setEnabled(isValid); + } + + @Override + public void registerOnPageChangeCallback(ViewPager2.OnPageChangeCallback callback) { + binding.viewPager.registerOnPageChangeCallback(callback); + } + + @Override + public void removeOnPageChangeCallback(ViewPager2.OnPageChangeCallback callback) { + binding.viewPager.unregisterOnPageChangeCallback(callback); + } + + @Override + public void registerCancelCallback(CancelCallback cancelCallback) { + cancelCallbacks.add(cancelCallback); + } + + @Override + public void removeCancelCallback(CancelCallback cancelCallback) { + cancelCallbacks.remove(cancelCallback); + } + + @Override + public void setNavigationButtonHidden(boolean isHidden) { + binding.setupNextButton.setVisibility(isHidden ? GONE : VISIBLE); + } + + @Override + public void setCancelButtonHidden(boolean isHidden) { + binding.setupCancelButton.setVisibility(isHidden ? GONE : VISIBLE); + } + + @Override + public void onProviderSelected(Provider provider) { + this.provider = provider; + } + + @Override + public void onConfigurationSuccess() { + binding.viewPager.setCurrentItem(binding.viewPager.getCurrentItem() + 1); + } + + @Override + public Provider getSelectedProvider() { + return provider; + } + + @Override + public int getCurrentPosition() { + return currentPosition; + } + + @Override + public void onSetupFinished() { + Intent intent = getIntent(); + intent.putExtra(Provider.KEY, provider); + setResult(RESULT_OK, intent); + finish(); + } + + @Override + public void onError(String reasonToFail) { + binding.viewPager.setCurrentItem(0, false); + try { + FragmentTransaction fragmentTransaction = fragmentManager.removePreviousFragment(ProviderSetupFailedDialog.TAG); + DialogFragment newFragment; + try { + JSONObject errorJson = new JSONObject(reasonToFail); + newFragment = ProviderSetupFailedDialog.newInstance(provider, errorJson, false); + } catch (JSONException e) { + e.printStackTrace(); + newFragment = ProviderSetupFailedDialog.newInstance(provider, reasonToFail); + } catch (NullPointerException e) { + //reasonToFail was null + return; + } + newFragment.show(fragmentTransaction, ProviderSetupFailedDialog.TAG); + } catch (IllegalStateException e) { + e.printStackTrace(); + } + } + + @Override + public void retrySetUpProvider(@NonNull Provider provider) { + onProviderSelected(provider); + binding.viewPager.setCurrentItem(adapter.getFragmentPostion(CONFIGURE_PROVIDER_FRAGMENT)); + } + + @Override + public void cancelSettingUpProvider() { + cancel(); + } + + @Override + public void updateProviderDetails() { + provider.reset(); + deleteProviderDetailsFromPreferences(provider.getDomain()); + binding.viewPager.setCurrentItem(adapter.getFragmentPostion(CONFIGURE_PROVIDER_FRAGMENT)); + } + + @Override + public void addAndSelectNewProvider(String url) { + // ignore, not implemented anymore in new setup flow + } + + @Override + protected void onDestroy() { + super.onDestroy(); + adapter = null; + } +}
\ No newline at end of file |