diff options
-rw-r--r-- | app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java | 284 | ||||
-rw-r--r-- | app/src/main/java/se/leap/bitmaskclient/views/VpnStateImage.java | 16 | ||||
-rw-r--r-- | app/src/main/res/drawable/ic_menu_color_point.png | bin | 0 -> 2454 bytes | |||
-rw-r--r-- | app/src/main/res/drawable/ic_menu_default.png | bin | 0 -> 2454 bytes | |||
-rw-r--r-- | docker/android-ndk/Dockerfile | 1 |
5 files changed, 193 insertions, 108 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 251ed48f..6e9879dd 100644 --- a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java @@ -25,6 +25,8 @@ import android.content.res.Configuration; import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.os.Handler; +import android.support.annotation.NonNull; import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; @@ -51,13 +53,13 @@ import android.widget.ListView; import se.leap.bitmaskclient.utils.ConfigHelper; import se.leap.bitmaskclient.DrawerSettingsAdapter; import se.leap.bitmaskclient.DrawerSettingsAdapter.DrawerSettingsItem; -import se.leap.bitmaskclient.FragmentManagerEnhanced; -import se.leap.bitmaskclient.fragments.AlwaysOnDialog; import se.leap.bitmaskclient.EipFragment; +import se.leap.bitmaskclient.FragmentManagerEnhanced; import se.leap.bitmaskclient.Provider; import se.leap.bitmaskclient.ProviderListActivity; import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.fragments.AboutFragment; +import se.leap.bitmaskclient.fragments.AlwaysOnDialog; import se.leap.bitmaskclient.fragments.LogFragment; import static android.content.Context.MODE_PRIVATE; @@ -97,24 +99,28 @@ public class NavigationDrawerFragment extends Fragment { * expands it. This shared preference tracks this. */ private static final String PREF_USER_LEARNED_DRAWER = "navigation_drawer_learned"; + private static final String TAG = NavigationDrawerFragment.class.getName(); + public static final int TWO_SECONDS = 2000; + public static final int THREE_SECONDS = 3500; /** * Helper component that ties the action bar to the navigation drawer. */ - private ActionBarDrawerToggle mDrawerToggle; + private ActionBarDrawerToggle drawerToggle; - private DrawerLayout mDrawerLayout; - private View mDrawerView; - private ListView mDrawerSettingsListView; - private ListView mDrawerAccountsListView; - private View mFragmentContainerView; + private DrawerLayout drawerLayout; + private View drawerView; + private ListView drawerAccountsListView; + private View fragmentContainerView; private ArrayAdapter<String> accountListAdapter; private DrawerSettingsAdapter settingsListAdapter; + private Toolbar toolbar; - private boolean mFromSavedInstanceState; - private boolean mUserLearnedDrawer; + private boolean userLearnedDrawer; + private volatile boolean wasPaused; + private volatile boolean shouldCloseOnResume; - private String mTitle; + private String title; private SharedPreferences preferences; @@ -122,38 +128,48 @@ public class NavigationDrawerFragment extends Fragment { private boolean showEnableExperimentalFeature = false; AlertDialog alertDialog; - public NavigationDrawerFragment() { - } - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - // Read in the flag indicating whether or not the user has demonstrated awareness of the + // Reads in the flag indicating whether or not the user has demonstrated awareness of the // drawer. See PREF_USER_LEARNED_DRAWER for details. preferences = getContext().getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); - mUserLearnedDrawer = preferences.getBoolean(PREF_USER_LEARNED_DRAWER, false); - if (savedInstanceState != null) { - mFromSavedInstanceState = true; - } + userLearnedDrawer = preferences.getBoolean(PREF_USER_LEARNED_DRAWER, false); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - // Indicate that this fragment would like to influence the set of actions in the action bar. + // Indicates that this fragment would like to influence the set of actions in the action bar. setHasOptionsMenu(true); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - mDrawerView = inflater.inflate(R.layout.f_drawer_main, container, false); + drawerView = inflater.inflate(R.layout.f_drawer_main, container, false); restoreFromSavedInstance(savedInstanceState); - return mDrawerView; + return drawerView; } public boolean isDrawerOpen() { - return mDrawerLayout != null && mDrawerLayout.isDrawerOpen(mFragmentContainerView); + return drawerLayout != null && drawerLayout.isDrawerOpen(fragmentContainerView); + } + + @Override + public void onResume() { + super.onResume(); + wasPaused = false; + if (shouldCloseOnResume) { + closeDrawerWithDelay(); + showDottedIconWithDelay(); + } + } + + @Override + public void onPause() { + super.onPause(); + wasPaused = true; } /** @@ -163,70 +179,44 @@ public class NavigationDrawerFragment extends Fragment { * @param drawerLayout The DrawerLayout containing this fragment's UI. */ public void setUp(int fragmentId, DrawerLayout drawerLayout) { - AppCompatActivity activity = (AppCompatActivity) getActivity(); - ActionBar actionBar = activity.getSupportActionBar(); - - mDrawerSettingsListView = mDrawerView.findViewById(R.id.settingsList); - mDrawerSettingsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - selectItem(parent, position); - } - }); - settingsListAdapter = new DrawerSettingsAdapter(getLayoutInflater()); - if (getContext() != null) { - settingsListAdapter.addItem(getSwitchInstance(getString(R.string.save_battery), - getSaveBattery(getContext()), - BATTERY_SAVER, - new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean newStateIsChecked) { - onSwitchItemSelected(BATTERY_SAVER, newStateIsChecked); - } - })); - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - settingsListAdapter.addItem(getSimpleTextInstance(getString(R.string.always_on_vpn), ALWAYS_ON)); - } - settingsListAdapter.addItem(getSimpleTextInstance(getString(switch_provider_menu_option), SWITCH_PROVIDER)); - settingsListAdapter.addItem(getSimpleTextInstance(getString(log_fragment_title), LOG)); - settingsListAdapter.addItem(getSimpleTextInstance(getString(about_fragment_title), ABOUT)); - if (ENABLE_DONATION) { - settingsListAdapter.addItem(getSimpleTextInstance(getString(donate_title), DONATE)); - } - - mDrawerSettingsListView.setAdapter(settingsListAdapter); - - mDrawerAccountsListView = mDrawerView.findViewById(R.id.accountList); - mDrawerAccountsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - selectItem(parent, position); - } - }); + final AppCompatActivity activity = (AppCompatActivity) getActivity(); + fragmentContainerView = activity.findViewById(fragmentId); + this.drawerLayout = drawerLayout; + // set a custom shadow that overlays the main content when the drawer opens + this.drawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); + toolbar = this.drawerLayout.findViewById(R.id.toolbar); + final ActionBar actionBar = setupActionBar(); + setupSettingsListAdapter(); + setupSettingsListView(); accountListAdapter = new ArrayAdapter<>(actionBar.getThemedContext(), R.layout.v_single_list_item, android.R.id.text1); + refreshAccountListAdapter(); + setupAccountsListView(); + setupActionBarDrawerToggle(activity); - createListAdapterData(); - - mDrawerAccountsListView.setAdapter(accountListAdapter); - - mFragmentContainerView = activity.findViewById(fragmentId); - mDrawerLayout = drawerLayout; + if (!userLearnedDrawer) { + openNavigationDrawerForFirstTimeUsers(); + } - // set a custom shadow that overlays the main content when the drawer opens - mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); + // Defer code dependent on restoration of previous instance state. + this.drawerLayout.post(new Runnable() { + @Override + public void run() { + drawerToggle.syncState(); + } + }); + this.drawerLayout.addDrawerListener(drawerToggle); + } - actionBar.setDisplayHomeAsUpEnabled(true); - actionBar.setHomeButtonEnabled(true); + private void setupActionBarDrawerToggle(final AppCompatActivity activity) { // ActionBarDrawerToggle ties together the the proper interactions // between the navigation drawer and the action bar app icon. - mDrawerToggle = new ActionBarDrawerToggle( - getActivity(), - mDrawerLayout, - (Toolbar) drawerLayout.findViewById(R.id.toolbar), + drawerToggle = new ActionBarDrawerToggle( + activity, + drawerLayout, + toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close ) { @@ -236,8 +226,7 @@ public class NavigationDrawerFragment extends Fragment { if (!isAdded()) { return; } - - getActivity().invalidateOptionsMenu(); // calls onPrepareOptionsMenu() + activity.invalidateOptionsMenu(); } @Override @@ -247,40 +236,120 @@ public class NavigationDrawerFragment extends Fragment { return; } - if (!mUserLearnedDrawer) { + if (!userLearnedDrawer) { // The user manually opened the drawer; store this flag to prevent auto-showing // the navigation drawer automatically in the future. - mUserLearnedDrawer = true; + userLearnedDrawer = true; preferences.edit().putBoolean(PREF_USER_LEARNED_DRAWER, true).apply(); + toolbar.setNavigationIcon(R.drawable.ic_menu_default); } - - getActivity().invalidateOptionsMenu(); // calls onPrepareOptionsMenu() + activity.invalidateOptionsMenu(); } }; + } + + private void setupAccountsListView() { + drawerAccountsListView = drawerView.findViewById(R.id.accountList); + drawerAccountsListView.setAdapter(accountListAdapter); + drawerAccountsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + selectItem(parent, position); + } + }); + } - // If the user hasn't 'learned' about the drawer, open it to introduce them to the drawer, - // per the navigation drawer design guidelines. - if (!mUserLearnedDrawer && !mFromSavedInstanceState) { - mDrawerLayout.openDrawer(mFragmentContainerView); + private void setupSettingsListView() { + ListView drawerSettingsListView = drawerView.findViewById(R.id.settingsList); + drawerSettingsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + selectItem(parent, position); + } + }); + drawerSettingsListView.setAdapter(settingsListAdapter); + } + + private void setupSettingsListAdapter() { + settingsListAdapter = new DrawerSettingsAdapter(getLayoutInflater()); + if (getContext() != null) { + settingsListAdapter.addItem(getSwitchInstance(getString(R.string.save_battery), + getSaveBattery(getContext()), + BATTERY_SAVER, + new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean newStateIsChecked) { + onSwitchItemSelected(BATTERY_SAVER, newStateIsChecked); + } + })); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + settingsListAdapter.addItem(getSimpleTextInstance(getString(R.string.always_on_vpn), ALWAYS_ON)); } + settingsListAdapter.addItem(getSimpleTextInstance(getString(switch_provider_menu_option), SWITCH_PROVIDER)); + settingsListAdapter.addItem(getSimpleTextInstance(getString(log_fragment_title), LOG)); + if (ENABLE_DONATION) { + settingsListAdapter.addItem(getSimpleTextInstance(getString(donate_title), DONATE)); + } + settingsListAdapter.addItem(getSimpleTextInstance(getString(about_fragment_title), ABOUT)); + } - // Defer code dependent on restoration of previous instance state. - mDrawerLayout.post(new Runnable() { + private ActionBar setupActionBar() { + AppCompatActivity activity = (AppCompatActivity) getActivity(); + activity.setSupportActionBar(toolbar); + final ActionBar actionBar = activity.getSupportActionBar(); + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setHomeButtonEnabled(true); + return actionBar; + } + + private void openNavigationDrawerForFirstTimeUsers() { + if (userLearnedDrawer) { + return; + } + + drawerLayout.openDrawer(fragmentContainerView, false); + closeDrawerWithDelay(); + showDottedIconWithDelay(); + + } + + private void showDottedIconWithDelay() { + final Handler navigationDrawerHandler = new Handler(); + navigationDrawerHandler.postDelayed(new Runnable() { @Override public void run() { - mDrawerToggle.syncState(); + if (!wasPaused) { + toolbar.setNavigationIcon(R.drawable.ic_menu_color_point); + toolbar.playSoundEffect(android.view.SoundEffectConstants.CLICK); + } + } - }); - mDrawerLayout.addDrawerListener(mDrawerToggle); + }, THREE_SECONDS); + } + @NonNull + private void closeDrawerWithDelay() { + final Handler navigationDrawerHandler = new Handler(); + navigationDrawerHandler.postDelayed(new Runnable() { + @Override + public void run() { + if (!wasPaused) { + drawerLayout.closeDrawer(fragmentContainerView, true); + } else { + shouldCloseOnResume = true; + } + + } + }, TWO_SECONDS); } private void selectItem(AdapterView<?> list, int position) { if (list != null) { ((ListView) list).setItemChecked(position, true); } - if (mDrawerLayout != null) { - mDrawerLayout.closeDrawer(mFragmentContainerView); + if (drawerLayout != null) { + drawerLayout.closeDrawer(fragmentContainerView); } onTextItemSelected(list, position); } @@ -359,12 +428,12 @@ public class NavigationDrawerFragment extends Fragment { public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // Forward the new configuration the drawer toggle component. - mDrawerToggle.onConfigurationChanged(newConfig); + drawerToggle.onConfigurationChanged(newConfig); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - if (mDrawerLayout != null && isDrawerOpen()) { + if (drawerLayout != null && isDrawerOpen()) { showGlobalContextActionBar(); } super.onCreateOptionsMenu(menu, inflater); @@ -372,10 +441,9 @@ public class NavigationDrawerFragment extends Fragment { @Override public boolean onOptionsItemSelected(MenuItem item) { - if (mDrawerToggle.onOptionsItemSelected(item)) { + if (drawerToggle.onOptionsItemSelected(item)) { return true; } - return super.onOptionsItemSelected(item); } @@ -430,8 +498,8 @@ public class NavigationDrawerFragment extends Fragment { Fragment fragment = null; String fragmentTag = null; - if (parent == mDrawerAccountsListView) { - mTitle = getString(R.string.vpn_fragment_title); + if (parent == drawerAccountsListView) { + title = getString(R.string.vpn_fragment_title); fragment = new EipFragment(); fragmentTag = EipFragment.TAG; Bundle arguments = new Bundle(); @@ -439,18 +507,18 @@ public class NavigationDrawerFragment extends Fragment { arguments.putParcelable(PROVIDER_KEY, currentProvider); fragment.setArguments(arguments); } else { - Log.d("Drawer", String.format("Selected position %d", position)); + Log.d(TAG, String.format("Selected position %d", position)); DrawerSettingsItem settingsItem = settingsListAdapter.getItem(position); switch (settingsItem.getItemType()) { case SWITCH_PROVIDER: getActivity().startActivityForResult(new Intent(getActivity(), ProviderListActivity.class), REQUEST_CODE_SWITCH_PROVIDER); break; case LOG: - mTitle = getString(log_fragment_title); + title = getString(log_fragment_title); fragment = new LogFragment(); break; case ABOUT: - mTitle = getString(about_fragment_title); + title = getString(about_fragment_title); fragment = new AboutFragment(); break; case ALWAYS_ON: @@ -484,18 +552,18 @@ public class NavigationDrawerFragment extends Fragment { ActionBar actionBar = getActionBar(); if (actionBar != null) { actionBar.setDisplayShowTitleEnabled(true); - actionBar.setSubtitle(mTitle); + actionBar.setSubtitle(title); } } public void refresh() { - createListAdapterData(); + refreshAccountListAdapter(); accountListAdapter.notifyDataSetChanged(); - mDrawerAccountsListView.setAdapter(accountListAdapter); + drawerAccountsListView.setAdapter(accountListAdapter); } - private void createListAdapterData() { + private void refreshAccountListAdapter() { accountListAdapter.clear(); String providerName = getProviderName(preferences); if (providerName == null) { diff --git a/app/src/main/java/se/leap/bitmaskclient/views/VpnStateImage.java b/app/src/main/java/se/leap/bitmaskclient/views/VpnStateImage.java index 2efd83d6..86761642 100644 --- a/app/src/main/java/se/leap/bitmaskclient/views/VpnStateImage.java +++ b/app/src/main/java/se/leap/bitmaskclient/views/VpnStateImage.java @@ -1,3 +1,19 @@ +/** + * Copyright (c) 2018 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ package se.leap.bitmaskclient.views; import android.content.Context; diff --git a/app/src/main/res/drawable/ic_menu_color_point.png b/app/src/main/res/drawable/ic_menu_color_point.png Binary files differnew file mode 100644 index 00000000..ef4b0e51 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_color_point.png diff --git a/app/src/main/res/drawable/ic_menu_default.png b/app/src/main/res/drawable/ic_menu_default.png Binary files differnew file mode 100644 index 00000000..e0d29163 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_default.png diff --git a/docker/android-ndk/Dockerfile b/docker/android-ndk/Dockerfile index 2872d98f..8e7a124b 100644 --- a/docker/android-ndk/Dockerfile +++ b/docker/android-ndk/Dockerfile @@ -37,3 +37,4 @@ ENV PATH ${PATH}:${ANDROID_NDK_HOME} # Accept all licenses RUN yes | sdkmanager --licenses RUN sdkmanager --list + |