summaryrefslogtreecommitdiff
path: root/app/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java')
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java30
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EIP.java4
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java11
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java164
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java27
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/views/IconCheckboxEntry.java91
6 files changed, 237 insertions, 90 deletions
diff --git a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java
index 3144d62c..85c982df 100644
--- a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java
@@ -60,6 +60,7 @@ import se.leap.bitmaskclient.fragments.AboutFragment;
import se.leap.bitmaskclient.fragments.AlwaysOnDialog;
import se.leap.bitmaskclient.fragments.ExcludeAppsFragment;
import se.leap.bitmaskclient.fragments.LogFragment;
+import se.leap.bitmaskclient.fragments.TetheringDialog;
import se.leap.bitmaskclient.utils.PreferenceHelper;
import se.leap.bitmaskclient.views.IconSwitchEntry;
import se.leap.bitmaskclient.views.IconTextEntry;
@@ -236,6 +237,7 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen
initUseBridgesEntry();
initSaveBatteryEntry();
initAlwaysOnVpnEntry();
+ initTetheringEntry();
initExcludeAppsEntry();
initDonateEntry();
initLogEntry();
@@ -321,6 +323,19 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen
}
}
+ private void initTetheringEntry() {
+ IconTextEntry tethering = drawerView.findViewById(R.id.tethering);
+ if (PreferenceHelper.hasSuPermission(getContext())) {
+ tethering.setVisibility(VISIBLE);
+ tethering.setOnClickListener((buttonView) -> {
+ showTetheringAlert();
+ });
+ } else {
+ tethering.setVisibility(GONE);
+ }
+
+ }
+
private void initExcludeAppsEntry() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
IconTextEntry excludeApps = drawerView.findViewById(R.id.exclude_apps);
@@ -442,13 +457,26 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen
saveBattery(getContext(), true);
})
.setNegativeButton(activity.getString(android.R.string.no), (dialog, which) -> saveBattery.setCheckedQuietly(false))
- .setOnDismissListener(dialog -> showEnableExperimentalFeature = false)
+ .setOnDismissListener(dialog -> showSaveBattery = false)
.setOnCancelListener(dialog -> saveBattery.setCheckedQuietly(false)).show();
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
+ public void showTetheringAlert() {
+ try {
+
+ FragmentTransaction fragmentTransaction = new FragmentManagerEnhanced(
+ getActivity().getSupportFragmentManager()).removePreviousFragment(
+ TetheringDialog.TAG);
+ DialogFragment newFragment = new TetheringDialog();
+ newFragment.show(fragmentTransaction, TetheringDialog.TAG);
+ } catch (IllegalStateException | NullPointerException e) {
+ e.printStackTrace();
+ }
+ }
+
public void showAlwaysOnDialog() {
try {
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
index 1d67cd12..1186b54f 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
@@ -61,6 +61,7 @@ import static de.blinkt.openvpn.core.connection.Connection.TransportType.OPENVPN
import static se.leap.bitmaskclient.Constants.BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT;
import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY;
import static se.leap.bitmaskclient.Constants.EIP_ACTION_CHECK_CERT_VALIDITY;
+import static se.leap.bitmaskclient.Constants.EIP_ACTION_CONFIGURE_TETHERING;
import static se.leap.bitmaskclient.Constants.EIP_ACTION_IS_RUNNING;
import static se.leap.bitmaskclient.Constants.EIP_ACTION_START;
import static se.leap.bitmaskclient.Constants.EIP_ACTION_START_ALWAYS_ON_VPN;
@@ -190,6 +191,9 @@ public final class EIP extends JobIntentService implements Observer {
disconnect();
earlyRoutes();
break;
+ case EIP_ACTION_CONFIGURE_TETHERING:
+ Log.d(TAG, "TODO: implement tethering configuration");
+ break;
}
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java
index eb266e3c..d2667e42 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java
@@ -6,6 +6,7 @@ import android.content.Intent;
import android.os.ResultReceiver;
import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
+import android.support.v4.content.ContextCompat;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -13,6 +14,7 @@ import org.jetbrains.annotations.Nullable;
import se.leap.bitmaskclient.Provider;
import static se.leap.bitmaskclient.Constants.EIP_ACTION_CHECK_CERT_VALIDITY;
+import static se.leap.bitmaskclient.Constants.EIP_ACTION_CONFIGURE_TETHERING;
import static se.leap.bitmaskclient.Constants.EIP_ACTION_START;
import static se.leap.bitmaskclient.Constants.EIP_ACTION_START_BLOCKING_VPN;
import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP;
@@ -90,4 +92,13 @@ public class EipCommand {
execute(context, EIP_ACTION_CHECK_CERT_VALIDITY, resultReceiver, null);
}
+ public static void configureTethering(@NonNull Context context) {
+ execute(context, EIP_ACTION_CONFIGURE_TETHERING);
+ }
+
+ @VisibleForTesting
+ public static void configureTethering(@NonNull Context context, ResultReceiver resultReceiver) {
+ execute(context, EIP_ACTION_CONFIGURE_TETHERING);
+ }
+
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java b/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java
index 90e3c5a1..96f7f0d4 100644
--- a/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java
+++ b/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java
@@ -2,48 +2,113 @@ package se.leap.bitmaskclient.fragments;
import android.app.Dialog;
import android.content.Intent;
-import android.os.Build;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
+import android.provider.Settings;
import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatDialogFragment;
import android.support.v7.widget.AppCompatTextView;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.method.LinkMovementMethod;
+import android.text.style.ClickableSpan;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
-import android.widget.CheckBox;
+import android.view.ViewGroup;
import butterknife.ButterKnife;
import butterknife.InjectView;
import se.leap.bitmaskclient.R;
-import se.leap.bitmaskclient.views.IconTextView;
-
-import static se.leap.bitmaskclient.utils.PreferenceHelper.saveShowAlwaysOnDialog;
-
+import se.leap.bitmaskclient.eip.EipCommand;
+import se.leap.bitmaskclient.utils.PreferenceHelper;
+import se.leap.bitmaskclient.views.IconCheckboxEntry;
/**
* Created by cyberta on 25.02.18.
*/
-
-
public class TetheringDialog extends AppCompatDialogFragment {
public final static String TAG = TetheringDialog.class.getName();
- @InjectView(R.id.do_not_show_again)
- CheckBox doNotShowAgainCheckBox;
+ @InjectView(R.id.tvTitle)
+ AppCompatTextView title;
@InjectView(R.id.user_message)
- IconTextView userMessage;
+ AppCompatTextView userMessage;
- @InjectView(R.id.block_vpn_user_message)
- AppCompatTextView blockVpnUserMessage;
+ @InjectView(R.id.selection_list_view)
+ RecyclerView selectionListView;
+ DialogListAdapter adapter;
+ private DialogListAdapter.ViewModel[] dataset;
+ public static class DialogListAdapter extends RecyclerView.Adapter<DialogListAdapter.ViewHolder> {
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
+ interface OnItemClickListener {
+ void onItemClick(ViewModel item);
+ }
+
+ private ViewModel[] dataSet;
+ private OnItemClickListener clickListener;
+
+ public DialogListAdapter(ViewModel[] dataSet, OnItemClickListener clickListener) {
+ this.dataSet = dataSet;
+ this.clickListener = clickListener;
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
+ IconCheckboxEntry v = new IconCheckboxEntry(viewGroup.getContext());
+ return new ViewHolder(v);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) {
+ viewHolder.bind(dataSet[i], clickListener);
+ }
+
+ @Override
+ public int getItemCount() {
+ return dataSet.length;
+ }
+
+ public static class ViewModel {
+
+ public Drawable image;
+ public String text;
+ public boolean checked;
+
+ ViewModel(Drawable image, String text, boolean checked) {
+ Log.d(TAG, "init ViewModel with string: " + text + " checked: " + checked);
+
+ this.image = image;
+ this.text = text;
+ this.checked = checked;
+ }
+ }
+
+ public static class ViewHolder extends RecyclerView.ViewHolder {
+
+ ViewHolder(IconCheckboxEntry v) {
+ super(v);
+ }
+
+ public void bind(ViewModel model, OnItemClickListener onClickListener) {
+ Log.d(TAG, "bind ViewModel");
+
+ ((IconCheckboxEntry) this.itemView).bind(model);
+ this.itemView.setOnClickListener(v -> {
+ model.checked = !model.checked;
+ ((IconCheckboxEntry) itemView).setChecked(model.checked);
+ onClickListener.onItemClick(model);
+ });
+ }
+ }
}
@NonNull
@@ -51,26 +116,67 @@ public class TetheringDialog extends AppCompatDialogFragment {
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
- View view = inflater.inflate(R.layout.d_checkbox_confirm, null);
+ View view = inflater.inflate(R.layout.d_list_selection, null);
ButterKnife.inject(this, view);
- userMessage.setIcon(R.drawable.ic_settings);
- userMessage.setText(getString(R.string.always_on_vpn_user_message));
+ title.setText(R.string.tethering);
+ userMessage.setMovementMethod(LinkMovementMethod.getInstance());
+ userMessage.setLinkTextColor(getContext().getResources().getColor(R.color.colorPrimary));
+ userMessage.setText(createUserMessage());
+
+ initDataset();
+ adapter = new DialogListAdapter(dataset, this::onItemClick);
+ selectionListView.setAdapter(adapter);
+ selectionListView.setLayoutManager(new LinearLayoutManager(getActivity()));
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- blockVpnUserMessage.setVisibility(View.VISIBLE);
- }
builder.setView(view)
.setPositiveButton(android.R.string.ok, (dialog, id) -> {
- if (doNotShowAgainCheckBox.isChecked()) {
- saveShowAlwaysOnDialog(getContext(), false);
- }
- Intent intent = new Intent("android.net.vpn.SETTINGS");
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(intent);
+ PreferenceHelper.wifiTethering(getContext(), dataset[0].checked);
+ PreferenceHelper.usbTethering(getContext(), dataset[1].checked);
+ PreferenceHelper.bluetoothTethering(getContext(), dataset[2].checked);
+ EipCommand.configureTethering(getContext());
})
.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.cancel());
return builder.create();
}
+
+ public void onItemClick(DialogListAdapter.ViewModel item) {
+
+ }
+
+ private CharSequence createUserMessage() {
+ String tetheringMessage = getString(R.string.tethering_message);
+ String systemSettings = getString(R.string.tethering_system_settings);
+ String systemSettingsMessage = getString(R.string.tethering_enabled_message, systemSettings);
+ String wholeMessage = systemSettingsMessage + "\n\n" + tetheringMessage;
+ int startIndex = wholeMessage.indexOf(systemSettings, 0);
+ int endIndex = startIndex + systemSettings.length();
+
+ Spannable spannable = new SpannableString(wholeMessage);
+ spannable.setSpan(new ClickableSpan() {
+ @Override
+ public void onClick(@NonNull View widget) {
+ Intent intent = new Intent(Settings.ACTION_WIRELESS_SETTINGS);
+ startActivity(intent);
+ }
+ }, startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ return spannable;
+ }
+
+ private void initDataset() {
+ dataset = new DialogListAdapter.ViewModel[] {
+ new DialogListAdapter.ViewModel(getContext().getResources().getDrawable(R.drawable.ic_wifi),
+ getContext().getString(R.string.tethering_wifi),
+ PreferenceHelper.getWifiTethering(getContext())),
+ new DialogListAdapter.ViewModel(getContext().getResources().getDrawable(R.drawable.ic_usb),
+ getContext().getString(R.string.tethering_usb),
+ PreferenceHelper.getUsbTethering(getContext())),
+ new DialogListAdapter.ViewModel(getContext().getResources().getDrawable(R.drawable.ic_bluetooth),
+ getContext().getString(R.string.tethering_bluetooth),
+ PreferenceHelper.getBluetoothTethering(getContext()))
+ };
+ }
+
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java b/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java
index de2058c7..25e4b797 100644
--- a/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java
@@ -32,6 +32,9 @@ import static se.leap.bitmaskclient.Constants.PROVIDER_PRIVATE_KEY;
import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE;
import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES;
import static se.leap.bitmaskclient.Constants.SU_PERMISSION;
+import static se.leap.bitmaskclient.Constants.TETHERING_BLUETOOTH;
+import static se.leap.bitmaskclient.Constants.TETHERING_USB;
+import static se.leap.bitmaskclient.Constants.TETHERING_WIFI;
import static se.leap.bitmaskclient.Constants.USE_PLUGGABLE_TRANSPORTS;
/**
@@ -146,6 +149,30 @@ public class PreferenceHelper {
return getBoolean(context, DEFAULT_SHARED_PREFS_BATTERY_SAVER, false);
}
+ public static void usbTethering(Context context, boolean isEnabled) {
+ putBoolean(context, TETHERING_USB, isEnabled);
+ }
+
+ public static boolean getUsbTethering(Context context) {
+ return getBoolean(context, TETHERING_USB, false);
+ }
+
+ public static void wifiTethering(Context context, boolean isEnabled) {
+ putBoolean(context, TETHERING_WIFI, isEnabled);
+ }
+
+ public static boolean getWifiTethering(Context context) {
+ return getBoolean(context, TETHERING_WIFI, false);
+ }
+
+ public static void bluetoothTethering(Context context, boolean isEnabled) {
+ putBoolean(context, TETHERING_BLUETOOTH, isEnabled);
+ }
+
+ public static boolean getBluetoothTethering(Context context) {
+ return getBoolean(context, TETHERING_BLUETOOTH, false);
+ }
+
public static void saveShowAlwaysOnDialog(Context context, boolean showAlwaysOnDialog) {
putBoolean(context, ALWAYS_ON_SHOW_DIALOG, showAlwaysOnDialog);
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/views/IconCheckboxEntry.java b/app/src/main/java/se/leap/bitmaskclient/views/IconCheckboxEntry.java
index cd151885..933d391b 100644
--- a/app/src/main/java/se/leap/bitmaskclient/views/IconCheckboxEntry.java
+++ b/app/src/main/java/se/leap/bitmaskclient/views/IconCheckboxEntry.java
@@ -2,45 +2,51 @@ package se.leap.bitmaskclient.views;
import android.annotation.TargetApi;
import android.content.Context;
-import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
-import android.support.annotation.ColorRes;
-import android.support.annotation.DrawableRes;
import android.support.annotation.Nullable;
-import android.support.annotation.StringRes;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.graphics.drawable.DrawableCompat;
+import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
-import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
+import butterknife.ButterKnife;
+import butterknife.InjectView;
import se.leap.bitmaskclient.R;
+import se.leap.bitmaskclient.fragments.TetheringDialog;
-public class IconTextEntry extends LinearLayout {
+public class IconCheckboxEntry extends LinearLayout {
- private TextView textView;
- private ImageView iconView;
- private TextView subtitleView;
+ @InjectView(android.R.id.text1)
+ TextView textView;
- public IconTextEntry(Context context) {
+ @InjectView(R.id.material_icon)
+ AppCompatImageView iconView;
+
+ @InjectView(R.id.checked_icon)
+ AppCompatImageView checkedIcon;
+
+ public IconCheckboxEntry(Context context) {
super(context);
initLayout(context, null);
}
- public IconTextEntry(Context context, @Nullable AttributeSet attrs) {
+ public IconCheckboxEntry(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initLayout(context, attrs);
}
- public IconTextEntry(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ public IconCheckboxEntry(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initLayout(context, attrs);
}
@TargetApi(21)
- public IconTextEntry(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ public IconCheckboxEntry(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initLayout(context, attrs);
}
@@ -48,59 +54,24 @@ public class IconTextEntry extends LinearLayout {
void initLayout(Context context, AttributeSet attrs) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- View rootview = inflater.inflate(R.layout.v_icon_text_list_item, this, true);
- textView = rootview.findViewById(android.R.id.text1);
- subtitleView = rootview.findViewById(R.id.subtitle);
- iconView = rootview.findViewById(R.id.material_icon);
-
- if (attrs != null) {
- TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.IconTextEntry);
-
- String entryText = typedArray.getString(R.styleable.IconTextEntry_text);
- if (entryText != null) {
- textView.setText(entryText);
- }
-
- String subtitle = typedArray.getString(R.styleable.IconTextEntry_subtitle);
- if (subtitle != null) {
- subtitleView.setText(subtitle);
- subtitleView.setVisibility(VISIBLE);
- }
-
- Drawable drawable = typedArray.getDrawable(R.styleable.IconTextEntry_icon);
- if (drawable != null) {
- iconView.setImageDrawable(drawable);
- }
-
- typedArray.recycle();
- }
-
+ View rootview = inflater.inflate(R.layout.v_icon_select_text_list_item, this, true);
+ ButterKnife.inject(this, rootview);
- }
-
- public void setText(@StringRes int id) {
- textView.setText(id);
- }
-
- public void setSubtitle(String text) {
- subtitleView.setText(text);
- subtitleView.setVisibility(VISIBLE);
- }
-
- public void hideSubtitle() {
- subtitleView.setVisibility(GONE);
- }
+ Drawable checkIcon = DrawableCompat.wrap(getResources().getDrawable(R.drawable.ic_check_bold));
+ DrawableCompat.setTint(checkIcon, ContextCompat.getColor(getContext(), R.color.colorSuccess));
+ checkedIcon.setImageDrawable(checkIcon);
- public void setSubtitleColor(@ColorRes int color) {
- subtitleView.setTextColor(getContext().getResources().getColor(color));
}
- public void setText(CharSequence text) {
- textView.setText(text);
+ public void bind(TetheringDialog.DialogListAdapter.ViewModel model) {
+ textView.setText(model.text);
+ iconView.setImageDrawable(model.image);
+ setChecked(model.checked);
}
- public void setIcon(@DrawableRes int id) {
- iconView.setImageResource(id);
+ public void setChecked(boolean checked) {
+ checkedIcon.setVisibility(checked ? VISIBLE : GONE);
+ checkedIcon.setContentDescription(checked ? "selected" : "unselected");
}
}