Java tutorial
package com.pluscubed.velociraptor.settings; import android.Manifest; import android.annotation.SuppressLint; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.location.Location; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.provider.Settings; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.design.widget.Snackbar; import android.support.v4.app.ActivityCompat; import android.support.v4.app.NotificationCompat; import android.support.v4.app.ShareCompat; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.SwitchCompat; import android.support.v7.widget.Toolbar; import android.text.Html; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.Spinner; import android.widget.TextView; import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.MaterialDialog; import com.anjlab.android.iab.v3.BillingProcessor; import com.anjlab.android.iab.v3.SkuDetails; import com.anjlab.android.iab.v3.TransactionDetails; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.location.LocationServices; import com.pluscubed.velociraptor.AppDetectionService; import com.pluscubed.velociraptor.BuildConfig; import com.pluscubed.velociraptor.R; import com.pluscubed.velociraptor.settings.appselection.AppSelectionActivity; import com.pluscubed.velociraptor.ui.SpeedLimitService; import com.pluscubed.velociraptor.utils.PrefUtils; import com.pluscubed.velociraptor.utils.Utils; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Currency; import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; public class SettingsActivity extends AppCompatActivity { public static final int PENDING_SERVICE = 4; public static final int PENDING_SERVICE_CLOSE = 3; public static final int PENDING_SETTINGS = 2; public static final int NOTIFICATION_CONTROLS = 42; public static final String[] SUBSCRIPTIONS = new String[] { "sub_1", "sub_2" }; public static final String[] PURCHASES = new String[] { "badge_1", "badge_2", "badge_3", "badge_4", "badge_5" }; private static final int REQUEST_LOCATION = 105; @BindView(R.id.toolbar) Toolbar toolbar; //M Permissions @BindView(R.id.button_floating_enabled) Button enableFloatingButton; @BindView(R.id.image_floating_enabled) ImageView enabledFloatingImage; @BindView(R.id.button_location_enabled) Button enableLocationButton; @BindView(R.id.image_location_enabled) ImageView enabledLocationImage; //Activation @BindView(R.id.linear_auto_display) View appDetectionContainer; @BindView(R.id.switch_auto_display) SwitchCompat appDetectionSwitch; @BindView(R.id.linear_auto_display_options) ViewGroup appDetectionOptionsContainer; @BindView(R.id.image_service_enabled) ImageView enabledServiceImage; @BindView(R.id.button_enable_service) Button enableServiceButton; @BindView(R.id.linear_app_selection) ViewGroup appSelectionContainer; @BindView(R.id.image_app_selection) ImageView appSelectionImage; @BindView(R.id.button_app_selection) Button appSelectionButton; @BindView(R.id.linear_android_auto) ViewGroup androidAutoContainer; @BindView(R.id.image_android_auto) ImageView androidAutoImage; @BindView(R.id.switch_android_auto) SwitchCompat androidAutoSwitch; @BindView(R.id.switch_notif_controls) View notifControlsContainer; //General @BindView(R.id.switch_speedometer) SwitchCompat showSpeedometerSwitch; @BindView(R.id.switch_beep) SwitchCompat beepSwitch; @BindView(R.id.button_test_beep) Button testBeepButton; @BindView(R.id.linear_tolerance) LinearLayout toleranceView; @BindView(R.id.text_overview_tolerance) TextView toleranceOverview; @BindView(R.id.linear_size) LinearLayout sizeView; @BindView(R.id.text_overview_size) TextView sizeOverview; @BindView(R.id.linear_opacity) LinearLayout opacityView; @BindView(R.id.text_overview_opacity) TextView opacityOverview; @BindView(R.id.spinner_unit) Spinner unitSpinner; @BindView(R.id.spinner_style) Spinner styleSpinner; //Wrong info @BindView(R.id.open_openstreetmap) LinearLayout openStreetMapView; @BindView(R.id.check_coverage) LinearLayout checkCoverageView; //Advanced @BindView(R.id.switch_debugging) SwitchCompat debuggingSwitch; private NotificationManager notificationManager; private BillingProcessor billingProcessor; private GoogleApiClient googleApiClient; @Override protected void onDestroy() { if (billingProcessor != null) { billingProcessor.release(); } super.onDestroy(); } @SuppressWarnings("ConstantConditions") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_settings); ButterKnife.bind(this); setSupportActionBar(toolbar); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { View marshmallowPermissionsCard = findViewById(R.id.card_m_permissions); marshmallowPermissionsCard.setVisibility(View.GONE); } openStreetMapView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ShareCompat.IntentBuilder.from(SettingsActivity.this).setText("https://www.openstreetmap.org") .setType("text/plain").startChooser(); } }); checkCoverageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { googleApiClient = new GoogleApiClient.Builder(SettingsActivity.this) .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() { @Override @SuppressWarnings("MissingPermission") public void onConnected(@Nullable Bundle bundle) { String uriString = "http://product.itoworld.com/map/124"; if (Utils.isLocationPermissionGranted(SettingsActivity.this)) { Location lastLocation = LocationServices.FusedLocationApi .getLastLocation(googleApiClient); if (lastLocation != null) { uriString += "?lon=" + lastLocation.getLongitude() + "&lat=" + lastLocation.getLatitude() + "&zoom=12"; } } Intent intent = new Intent(); intent.setData(Uri.parse(uriString)); intent.setAction(Intent.ACTION_VIEW); try { startActivity(intent); } catch (ActivityNotFoundException e) { Snackbar.make(enableFloatingButton, R.string.open_coverage_map_failed, Snackbar.LENGTH_LONG).show(); } googleApiClient.disconnect(); } @Override public void onConnectionSuspended(int i) { } }).addApi(LocationServices.API).build(); googleApiClient.connect(); } }); notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); notifControlsContainer.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(SettingsActivity.this, SpeedLimitService.class); intent.putExtra(SpeedLimitService.EXTRA_NOTIF_START, true); PendingIntent pending = PendingIntent.getService(SettingsActivity.this, PENDING_SERVICE, intent, PendingIntent.FLAG_CANCEL_CURRENT); Intent intentClose = new Intent(SettingsActivity.this, SpeedLimitService.class); intentClose.putExtra(SpeedLimitService.EXTRA_NOTIF_CLOSE, true); PendingIntent pendingClose = PendingIntent.getService(SettingsActivity.this, PENDING_SERVICE_CLOSE, intentClose, PendingIntent.FLAG_CANCEL_CURRENT); Intent settings = new Intent(SettingsActivity.this, SettingsActivity.class); PendingIntent settingsIntent = PendingIntent.getActivity(SettingsActivity.this, PENDING_SETTINGS, settings, PendingIntent.FLAG_CANCEL_CURRENT); NotificationCompat.Builder builder = new NotificationCompat.Builder(SettingsActivity.this) .setSmallIcon(R.drawable.ic_speedometer) .setContentTitle(getString(R.string.controls_notif_title)) .setContentText(getString(R.string.controls_notif_desc)) .addAction(0, getString(R.string.show), pending) .addAction(0, getString(R.string.hide), pendingClose).setDeleteIntent(pendingClose) .setContentIntent(settingsIntent); Notification notification = builder.build(); notificationManager.notify(NOTIFICATION_CONTROLS, notification); } }); appSelectionButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(SettingsActivity.this, AppSelectionActivity.class)); } }); appDetectionSwitch.setChecked(PrefUtils.isAutoDisplayEnabled(this)); appDetectionContainer.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { appDetectionSwitch.toggle(); boolean autoDisplayEnabled = appDetectionSwitch.isChecked(); PrefUtils.setAutoDisplay(SettingsActivity.this, autoDisplayEnabled); updateAppDetectionOptionStates(); } }); enableServiceButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)); } catch (ActivityNotFoundException e) { Snackbar.make(enableServiceButton, R.string.open_settings_failed_accessibility, Snackbar.LENGTH_LONG).show(); } } }); enableFloatingButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { //Open the current default browswer App Info page openSettings(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, BuildConfig.APPLICATION_ID); } catch (ActivityNotFoundException ignored) { Snackbar.make(enableFloatingButton, R.string.open_settings_failed_overlay, Snackbar.LENGTH_LONG) .show(); } } }); enableLocationButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ActivityCompat.requestPermissions(SettingsActivity.this, new String[] { Manifest.permission.ACCESS_FINE_LOCATION }, REQUEST_LOCATION); } }); ArrayAdapter<String> unitAdapter = new ArrayAdapter<>(this, R.layout.spinner_item_text, new String[] { "mph", "km/h" }); unitAdapter.setDropDownViewResource(R.layout.support_simple_spinner_dropdown_item); unitSpinner.setAdapter(unitAdapter); unitSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { if (PrefUtils.getUseMetric(SettingsActivity.this) != (position == 1)) { PrefUtils.setUseMetric(SettingsActivity.this, position == 1); unitSpinner.setDropDownVerticalOffset(Utils.convertDpToPx(SettingsActivity.this, unitSpinner.getSelectedItemPosition() * -48)); Utils.updateFloatingServicePrefs(SettingsActivity.this); } } @Override public void onNothingSelected(AdapterView<?> parent) { } }); unitSpinner.setSelection(PrefUtils.getUseMetric(this) ? 1 : 0); unitSpinner .setDropDownVerticalOffset(Utils.convertDpToPx(this, unitSpinner.getSelectedItemPosition() * -48)); ArrayAdapter<String> styleAdapter = new ArrayAdapter<>(this, R.layout.spinner_item_text, new String[] { getString(R.string.united_states), getString(R.string.international) }); styleAdapter.setDropDownViewResource(R.layout.support_simple_spinner_dropdown_item); styleSpinner.setAdapter(styleAdapter); styleSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { if (position != PrefUtils.getSignStyle(SettingsActivity.this)) { PrefUtils.setSignStyle(SettingsActivity.this, position); styleSpinner.setDropDownVerticalOffset(Utils.convertDpToPx(SettingsActivity.this, styleSpinner.getSelectedItemPosition() * -48)); Utils.updateFloatingServicePrefs(SettingsActivity.this); } } @Override public void onNothingSelected(AdapterView<?> parent) { } }); styleSpinner.setSelection(PrefUtils.getSignStyle(this)); styleSpinner .setDropDownVerticalOffset(Utils.convertDpToPx(this, styleSpinner.getSelectedItemPosition() * -48)); toleranceView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new ToleranceDialogFragment().show(getFragmentManager(), "dialog_tolerance"); } }); sizeView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new SizeDialogFragment().show(getFragmentManager(), "dialog_size"); } }); opacityView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new OpacityDialogFragment().show(getFragmentManager(), "dialog_opacity"); } }); showSpeedometerSwitch.setChecked(PrefUtils.getShowSpeedometer(this)); ((View) showSpeedometerSwitch.getParent()).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { showSpeedometerSwitch.setChecked(!showSpeedometerSwitch.isChecked()); PrefUtils.setShowSpeedometer(SettingsActivity.this, showSpeedometerSwitch.isChecked()); Utils.updateFloatingServicePrefs(SettingsActivity.this); } }); debuggingSwitch.setChecked(PrefUtils.isDebuggingEnabled(this)); ((View) debuggingSwitch.getParent()).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { debuggingSwitch.setChecked(!debuggingSwitch.isChecked()); PrefUtils.setDebugging(SettingsActivity.this, debuggingSwitch.isChecked()); Utils.updateFloatingServicePrefs(SettingsActivity.this); } }); beepSwitch.setChecked(PrefUtils.isBeepAlertEnabled(this)); beepSwitch.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { PrefUtils.setBeepAlertEnabled(SettingsActivity.this, beepSwitch.isChecked()); } }); testBeepButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Utils.playBeep(); } }); androidAutoSwitch.setChecked(PrefUtils.isAutoIntegrationEnabled(this)); androidAutoContainer.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (BuildConfig.FLAVOR.equals("play")) { Snackbar.make(findViewById(android.R.id.content), R.string.auto_not_available, Snackbar.LENGTH_LONG).show(); return; } if (!androidAutoSwitch.isEnabled()) { return; } androidAutoSwitch.toggle(); boolean checked = androidAutoSwitch.isChecked(); if (checked) { new MaterialDialog.Builder(SettingsActivity.this) .content(R.string.android_auto_instruction_dialog).positiveText(android.R.string.ok) .onPositive(new MaterialDialog.SingleButtonCallback() { @Override public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { PrefUtils.setAutoIntegrationEnabled(SettingsActivity.this, true); } }).show(); } else { PrefUtils.setAutoIntegrationEnabled(SettingsActivity.this, checked); } } }); invalidateStates(); if (BuildConfig.VERSION_CODE > PrefUtils.getVersionCode(this) && !PrefUtils.isFirstRun(this)) { showChangelog(); } billingProcessor = new BillingProcessor(this, getString(R.string.play_license_key), new BillingProcessor.IBillingHandler() { @Override public void onProductPurchased(String productId, TransactionDetails details) { PrefUtils.setSupported(SettingsActivity.this, true); if (Arrays.asList(PURCHASES).contains(productId)) billingProcessor.consumePurchase(productId); } @Override public void onPurchaseHistoryRestored() { } @Override public void onBillingError(int errorCode, Throwable error) { if (errorCode != 110) { Snackbar.make(findViewById(android.R.id.content), "Billing error: code = " + errorCode + ", error: " + (error != null ? error.getMessage() : "?"), Snackbar.LENGTH_LONG).show(); } } @Override public void onBillingInitialized() { billingProcessor.loadOwnedPurchasesFromGoogle(); } }); PrefUtils.setFirstRun(this, false); PrefUtils.setVersionCode(this, BuildConfig.VERSION_CODE); } private void showSupportDialog() { String content = getString(BuildConfig.FLAVOR.equals("play") ? R.string.support_dev_dialog : R.string.support_dev_dialog_notplay); if (PrefUtils.hasSupported(this) || !billingProcessor.listOwnedSubscriptions().isEmpty()) { content += "\n\n\uD83C\uDF89 " + getString(R.string.support_dev_dialog_badge) + " \uD83C\uDF89"; } MaterialDialog.Builder builder = new MaterialDialog.Builder(this) .icon(Utils.getVectorDrawableCompat(this, R.drawable.ic_favorite_black_24dp)) .title(R.string.support_development).content(Html.fromHtml(content)); if (BuildConfig.FLAVOR.equals("play")) { if (!billingProcessor.isInitialized()) { Snackbar.make(findViewById(android.R.id.content), R.string.in_app_unavailable, Snackbar.LENGTH_SHORT).show(); return; } final List<SkuDetails> purchaseListingDetails = billingProcessor .getPurchaseListingDetails(new ArrayList<>(Arrays.asList(PURCHASES))); final List<SkuDetails> subscriptionListingDetails = billingProcessor .getSubscriptionListingDetails(new ArrayList<>(Arrays.asList(SUBSCRIPTIONS))); if (purchaseListingDetails == null || purchaseListingDetails.isEmpty()) { Snackbar.make(findViewById(android.R.id.content), R.string.in_app_unavailable, Snackbar.LENGTH_SHORT).show(); return; } purchaseListingDetails.addAll(0, subscriptionListingDetails); List<String> purchaseDisplay = new ArrayList<>(); for (SkuDetails details : purchaseListingDetails) { NumberFormat format = NumberFormat.getCurrencyInstance(); format.setCurrency(Currency.getInstance(details.currency)); String amount = format.format(details.priceValue); if (details.isSubscription) amount = String.format(getString(R.string.per_month), amount); else { amount = String.format(getString(R.string.one_time), amount); } purchaseDisplay.add(amount); } builder.items(purchaseDisplay).itemsCallback(new MaterialDialog.ListCallback() { @Override public void onSelection(MaterialDialog dialog, View itemView, int which, CharSequence text) { SkuDetails skuDetails = purchaseListingDetails.get(which); if (skuDetails.isSubscription) { billingProcessor.subscribe(SettingsActivity.this, skuDetails.productId); } else { billingProcessor.purchase(SettingsActivity.this, skuDetails.productId); } } }); } if (BuildConfig.FLAVOR.equals("full")) { builder.positiveText(R.string.dismiss); } builder.show(); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (!billingProcessor.handleActivityResult(requestCode, resultCode, data)) super.onActivityResult(requestCode, resultCode, data); } @Override protected void onPause() { super.onPause(); enableService(false); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_settings, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_settings_support: showSupportDialog(); return true; case R.id.menu_settings_about: showAboutDialog(); return true; case R.id.menu_settings_changelog: showChangelog(); return true; } return super.onOptionsItemSelected(item); } private void showAboutDialog() { new MaterialDialog.Builder(this).title(getString(R.string.about_dialog_title, BuildConfig.VERSION_NAME)) .positiveText(R.string.dismiss).content(Html.fromHtml(getString(R.string.about_body))) .iconRes(R.mipmap.ic_launcher).show(); } private void showChangelog() { ChangelogDialog.newInstance().show(getFragmentManager(), "CHANGELOG_DIALOG"); } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if (hasFocus) { invalidateStates(); } } private void invalidateStates() { boolean permissionGranted = Utils.isLocationPermissionGranted(this); enabledLocationImage .setImageResource(permissionGranted ? R.drawable.ic_done_green_40dp : R.drawable.ic_cross_red_40dp); enableLocationButton.setEnabled(!permissionGranted); @SuppressLint({ "NewApi", "LocalSuppress" }) boolean overlayEnabled = Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(this); enabledFloatingImage .setImageResource(overlayEnabled ? R.drawable.ic_done_green_40dp : R.drawable.ic_cross_red_40dp); enableFloatingButton.setEnabled(!overlayEnabled); updateAppDetectionOptionStates(); String constant = getString(PrefUtils.getUseMetric(this) ? R.string.kmph : R.string.mph, String.valueOf(PrefUtils.getSpeedingConstant(this))); String percent = getString(R.string.percent, String.valueOf(PrefUtils.getSpeedingPercent(this))); String mode = PrefUtils.getToleranceMode(this) ? "+" : getString(R.string.or); String overview = getString(R.string.tolerance_desc, percent, mode, constant); toleranceOverview.setText(overview); String limitSizePercent = getString(R.string.percent, String.valueOf((int) (PrefUtils.getSpeedLimitSize(this) * 100))); String speedLimitSize = getString(R.string.size_limit_overview, limitSizePercent); String speedometerSizePercent = getString(R.string.percent, String.valueOf((int) (PrefUtils.getSpeedometerSize(this) * 100))); String speedometerSize = getString(R.string.size_speedometer_overview, speedometerSizePercent); sizeOverview.setText(speedLimitSize + "\n" + speedometerSize); opacityOverview.setText(getString(R.string.percent, String.valueOf(PrefUtils.getOpacity(this)))); if (permissionGranted && overlayEnabled) { enableService(true); } } private void updateAppDetectionOptionStates() { boolean autoDisplayEnabled = PrefUtils.isAutoDisplayEnabled(this); enableDisableAllChildren(autoDisplayEnabled, appDetectionOptionsContainer); boolean serviceEnabled = Utils.isAccessibilityServiceEnabled(this, AppDetectionService.class); enabledServiceImage .setImageResource(serviceEnabled ? R.drawable.ic_done_green_40dp : R.drawable.ic_cross_red_40dp); enabledServiceImage.setAlpha(autoDisplayEnabled ? 1f : 0.7f); enableDisableAllChildren(autoDisplayEnabled && serviceEnabled, appSelectionContainer); appSelectionImage.setAlpha(autoDisplayEnabled && serviceEnabled ? 1f : 0.7f); enableDisableAllChildren(autoDisplayEnabled && serviceEnabled, androidAutoContainer); androidAutoImage.setAlpha(autoDisplayEnabled && serviceEnabled ? 1f : 0.7f); enableServiceButton.setEnabled(autoDisplayEnabled && !serviceEnabled); } private void enableDisableAllChildren(boolean enable, ViewGroup viewgroup) { for (int i = 0; i < viewgroup.getChildCount(); i++) { View child = viewgroup.getChildAt(i); child.setEnabled(enable); if (child instanceof ViewGroup) { enableDisableAllChildren(enable, (ViewGroup) child); } } } private void enableService(boolean start) { Intent intent = new Intent(this, SpeedLimitService.class); if (!start) { intent.putExtra(SpeedLimitService.EXTRA_CLOSE, true); } startService(intent); } private void openSettings(String settingsAction, String packageName) { Intent intent = new Intent(settingsAction); intent.setData(Uri.parse("package:" + packageName)); startActivity(intent); } }