From 00762c85e6afee1215ed55005655e002ad51a43d Mon Sep 17 00:00:00 2001 From: Ammar Githam Date: Thu, 10 Sep 2020 19:39:10 +0900 Subject: [PATCH] Add account switcher in MorePreferencesFragment --- .../awais/instagrabber/activities/Login.java | 41 ++-- .../instagrabber/activities/MainActivity.java | 5 +- .../adapters/AccountSwitcherListAdapter.java | 112 +++++++++ .../dialogs/QuickAccessDialog.java | 14 +- .../fragments/main/ProfileFragment.java | 21 +- .../settings/MorePreferencesFragment.java | 223 ++++++++++++++++-- .../settings/SettingsPreferencesFragment.java | 7 - .../repositories/ProfileRepository.java | 11 + .../repositories/responses/UserInfo.java | 41 ++++ .../services/AddCookiesInterceptor.java | 3 +- .../instagrabber/services/FeedService.java | 4 - .../services/FriendshipService.java | 2 +- .../services/LoggingInterceptor.java | 18 +- .../instagrabber/services/ProfileService.java | 69 ++++++ .../awais/instagrabber/utils/DataBox.java | 195 +++++++++++---- .../instagrabber/utils/ExportImportUtils.java | 49 ++-- .../utils/NavigationExtensions.java | 8 +- .../java/awais/instagrabber/utils/Utils.java | 1 + .../res/drawable/ic_arrow_drop_down_24.xml | 10 + app/src/main/res/drawable/ic_check_24.xml | 10 + .../main/res/layout/pref_account_switcher.xml | 65 +++++ app/src/main/res/values/arrays.xml | 1 - app/src/main/res/values/dimens.xml | 1 + app/src/main/res/values/strings.xml | 3 +- 24 files changed, 760 insertions(+), 154 deletions(-) create mode 100644 app/src/main/java/awais/instagrabber/adapters/AccountSwitcherListAdapter.java create mode 100644 app/src/main/java/awais/instagrabber/repositories/ProfileRepository.java create mode 100644 app/src/main/java/awais/instagrabber/repositories/responses/UserInfo.java delete mode 100644 app/src/main/java/awais/instagrabber/services/FeedService.java create mode 100644 app/src/main/java/awais/instagrabber/services/ProfileService.java create mode 100644 app/src/main/res/drawable/ic_arrow_drop_down_24.xml create mode 100644 app/src/main/res/drawable/ic_check_24.xml create mode 100644 app/src/main/res/layout/pref_account_switcher.xml diff --git a/app/src/main/java/awais/instagrabber/activities/Login.java b/app/src/main/java/awais/instagrabber/activities/Login.java index ee13138a..0e4c7fc5 100755 --- a/app/src/main/java/awais/instagrabber/activities/Login.java +++ b/app/src/main/java/awais/instagrabber/activities/Login.java @@ -3,7 +3,6 @@ package awais.instagrabber.activities; import android.annotation.SuppressLint; import android.content.Intent; import android.graphics.Bitmap; -import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.view.View; @@ -23,8 +22,6 @@ import awais.instagrabber.databinding.ActivityLoginBinding; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Utils; -import static awais.instagrabber.utils.Utils.settingsHelper; - public final class Login extends BaseLanguageActivity implements View.OnClickListener, CompoundButton.OnCheckedChangeListener { private final WebViewClient webViewClient = new WebViewClient() { @Override @@ -36,15 +33,23 @@ public final class Login extends BaseLanguageActivity implements View.OnClickLis public void onPageFinished(final WebView view, final String url) { webViewUrl = url; final String mainCookie = Utils.getCookie(url); - if (Utils.isEmpty(mainCookie) || !mainCookie.contains("; ds_user_id=")) ready = true; - else if (mainCookie.contains("; ds_user_id=") && ready) { - final Intent intent = new Intent(); - intent.putExtra("cookie", mainCookie); - setResult(Constants.LOGIN_RESULT_CODE, intent); - finish(); + if (Utils.isEmpty(mainCookie) || !mainCookie.contains("; ds_user_id=")) { + ready = true; + return; + } + if (mainCookie.contains("; ds_user_id=") && ready) { + returnCookieResult(mainCookie); } } }; + + private void returnCookieResult(final String mainCookie) { + final Intent intent = new Intent(); + intent.putExtra("cookie", mainCookie); + setResult(Constants.LOGIN_RESULT_CODE, intent); + finish(); + } + private final WebChromeClient webChromeClient = new WebChromeClient(); private String webViewUrl, defaultUserAgent; private boolean ready = false; @@ -67,16 +72,15 @@ public final class Login extends BaseLanguageActivity implements View.OnClickLis public void onClick(final View v) { if (v == loginBinding.refresh) { loginBinding.webView.loadUrl("https://instagram.com/"); - } else if (v == loginBinding.cookies) { + return; + } + if (v == loginBinding.cookies) { final String mainCookie = Utils.getCookie(webViewUrl); - if (Utils.isEmpty(mainCookie) || !mainCookie.contains("; ds_user_id=")) + if (Utils.isEmpty(mainCookie) || !mainCookie.contains("; ds_user_id=")) { Toast.makeText(this, R.string.login_error_loading_cookies, Toast.LENGTH_SHORT).show(); - else { - Utils.setupCookies(mainCookie); - settingsHelper.putString(Constants.COOKIE, mainCookie); - Toast.makeText(this, R.string.login_success_loading_cookies, Toast.LENGTH_SHORT).show(); - finish(); + return; } + returnCookieResult(mainCookie); } } @@ -84,8 +88,9 @@ public final class Login extends BaseLanguageActivity implements View.OnClickLis public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) { final WebSettings webSettings = loginBinding.webView.getSettings(); - final String newUserAgent = isChecked ? "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36" - : defaultUserAgent; + final String newUserAgent = isChecked + ? "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36" + : defaultUserAgent; webSettings.setUserAgentString(newUserAgent); webSettings.setUseWideViewPort(isChecked); diff --git a/app/src/main/java/awais/instagrabber/activities/MainActivity.java b/app/src/main/java/awais/instagrabber/activities/MainActivity.java index 0974cb0e..03b05587 100644 --- a/app/src/main/java/awais/instagrabber/activities/MainActivity.java +++ b/app/src/main/java/awais/instagrabber/activities/MainActivity.java @@ -109,7 +109,6 @@ public class MainActivity extends BaseLanguageActivity { setContentView(binding.getRoot()); final Toolbar toolbar = binding.toolbar; setSupportActionBar(toolbar); - isLoggedIn = !Utils.isEmpty(cookie) && Utils.getUserIdFromCookie(cookie) != null; if (savedInstanceState == null) { setupBottomNavigationBar(true); } @@ -315,6 +314,8 @@ public class MainActivity extends BaseLanguageActivity { private void setupBottomNavigationBar(final boolean setDefaultFromSettings) { int main_nav_ids = R.array.main_nav_ids; + final String cookie = settingsHelper.getString(Constants.COOKIE); + isLoggedIn = !Utils.isEmpty(cookie) && Utils.getUserIdFromCookie(cookie) != null; if (!isLoggedIn) { main_nav_ids = R.array.logged_out_main_nav_ids; binding.bottomNavView.getMenu().clear(); @@ -329,7 +330,7 @@ public class MainActivity extends BaseLanguageActivity { mainNavList.add(resourceId); } navIds.recycle(); - if (setDefaultFromSettings) { + if (setDefaultFromSettings || !isLoggedIn) { final String defaultTabIdString = settingsHelper.getString(Constants.DEFAULT_TAB); try { final int defaultNavId = Utils.isEmpty(defaultTabIdString) || !isLoggedIn diff --git a/app/src/main/java/awais/instagrabber/adapters/AccountSwitcherListAdapter.java b/app/src/main/java/awais/instagrabber/adapters/AccountSwitcherListAdapter.java new file mode 100644 index 00000000..daa66f44 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/adapters/AccountSwitcherListAdapter.java @@ -0,0 +1,112 @@ +package awais.instagrabber.adapters; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Typeface; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.List; + +import awais.instagrabber.R; +import awais.instagrabber.databinding.PrefAccountSwitcherBinding; +import awais.instagrabber.utils.Constants; +import awais.instagrabber.utils.DataBox; + +import static awais.instagrabber.utils.Utils.settingsHelper; + +public class AccountSwitcherListAdapter extends ArrayAdapter { + private static final String TAG = "AccountSwitcherListAdap"; + + private final OnAccountClickListener clickListener; + private final OnAccountLongClickListener longClickListener; + + public AccountSwitcherListAdapter(@NonNull final Context context, + final int resource, + @NonNull final List allUsers, + final OnAccountClickListener clickListener, + final OnAccountLongClickListener longClickListener) { + super(context, resource, allUsers); + this.clickListener = clickListener; + this.longClickListener = longClickListener; + } + + @NonNull + @Override + public View getView(final int position, @Nullable final View convertView, @NonNull final ViewGroup parent) { + final DataBox.CookieModel model = getItem(position); + final String cookie = settingsHelper.getString(Constants.COOKIE); + if (convertView == null) { + final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); + final PrefAccountSwitcherBinding binding = PrefAccountSwitcherBinding.inflate(layoutInflater, parent, false); + final ViewHolder viewHolder = new ViewHolder(binding); + viewHolder.itemView.setTag(viewHolder); + if (model == null) return viewHolder.itemView; + final boolean equals = model.getCookie().equals(cookie); + viewHolder.bind(model, equals, clickListener, longClickListener); + return viewHolder.itemView; + } + final ViewHolder viewHolder = (ViewHolder) convertView.getTag(); + if (model == null) return viewHolder.itemView; + final boolean equals = model.getCookie().equals(cookie); + viewHolder.bind(model, equals, clickListener, longClickListener); + return viewHolder.itemView; + } + + public interface OnAccountClickListener { + void onAccountClick(final DataBox.CookieModel model, final boolean isCurrent); + } + + public interface OnAccountLongClickListener { + boolean onAccountLongClick(final DataBox.CookieModel model, final boolean isCurrent); + } + + private static class ViewHolder { + private final View itemView; + private final PrefAccountSwitcherBinding binding; + + public ViewHolder(final PrefAccountSwitcherBinding binding) { + this.itemView = binding.getRoot(); + this.binding = binding; + binding.arrowDown.setImageResource(R.drawable.ic_check_24); + } + + @SuppressLint("SetTextI18n") + public void bind(final DataBox.CookieModel model, + final boolean isCurrent, + final OnAccountClickListener clickListener, + final OnAccountLongClickListener longClickListener) { + // Log.d(TAG, model.getFullName()); + itemView.setOnClickListener(v -> { + if (clickListener == null) return; + clickListener.onAccountClick(model, isCurrent); + }); + itemView.setOnLongClickListener(v -> { + if (longClickListener == null) return false; + return longClickListener.onAccountLongClick(model, isCurrent); + }); + binding.profilePic.setImageURI(model.getProfilePic()); + binding.username.setText("@" + model.getUsername()); + binding.fullName.setTypeface(null); + final String fullName = model.getFullName(); + if (TextUtils.isEmpty(fullName)) { + binding.fullName.setVisibility(View.GONE); + } else { + binding.fullName.setVisibility(View.VISIBLE); + binding.fullName.setText(fullName); + } + if (!isCurrent) { + binding.arrowDown.setVisibility(View.GONE); + return; + } + binding.fullName.setTypeface(binding.fullName.getTypeface(), Typeface.BOLD); + binding.arrowDown.setVisibility(View.VISIBLE); + } + } +} diff --git a/app/src/main/java/awais/instagrabber/dialogs/QuickAccessDialog.java b/app/src/main/java/awais/instagrabber/dialogs/QuickAccessDialog.java index 2480c240..39054fce 100755 --- a/app/src/main/java/awais/instagrabber/dialogs/QuickAccessDialog.java +++ b/app/src/main/java/awais/instagrabber/dialogs/QuickAccessDialog.java @@ -147,12 +147,14 @@ public final class QuickAccessDialog extends BottomSheetDialogFragment implement if (cookieModel.isSelected()) Toast.makeText(v.getContext(), R.string.quick_access_cannot_delete_curr, Toast.LENGTH_SHORT).show(); else - new AlertDialog.Builder(activity).setPositiveButton(R.string.yes, (d, which) -> { - Utils.dataBox.delUserCookie(cookieModel); - rvQuickAccess.findViewWithTag(cookieModel).setVisibility(View.GONE); - }) - .setNegativeButton(R.string.no, null).setMessage(getString(R.string.quick_access_confirm_delete, - cookieModel.getUsername())).show(); + new AlertDialog.Builder(activity) + .setMessage(getString(R.string.quick_access_confirm_delete, cookieModel.getUsername())) + .setPositiveButton(R.string.yes, (d, which) -> { + Utils.dataBox.delUserCookie(cookieModel); + rvQuickAccess.findViewWithTag(cookieModel).setVisibility(View.GONE); + }) + .setNegativeButton(R.string.no, null) + .show(); } return true; diff --git a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java index 3e4387e7..4d6de2c0 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java @@ -60,6 +60,7 @@ import awais.instagrabber.customviews.PrimaryActionModeCallback; import awais.instagrabber.customviews.PrimaryActionModeCallback.CallbacksHelper; import awais.instagrabber.customviews.helpers.GridAutofitLayoutManager; import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration; +import awais.instagrabber.customviews.helpers.NestedCoordinatorLayout; import awais.instagrabber.customviews.helpers.RecyclerLazyLoader; import awais.instagrabber.databinding.FragmentProfileBinding; import awais.instagrabber.dialogs.ProfilePicDialogFragment; @@ -107,6 +108,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe private MenuItem favMenuItem; private boolean isPullToRefresh; private HighlightsAdapter highlightsAdapter; + private HighlightsViewModel highlightsViewModel; private final Runnable usernameSettingRunnable = () -> { final ActionBar actionBar = fragmentActivity.getSupportActionBar(); @@ -197,7 +199,6 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe action.setUsername("@" + text); NavHostFragment.findNavController(this).navigate(action); }; - private HighlightsViewModel highlightsViewModel; @Override public void onCreate(@Nullable final Bundle savedInstanceState) { @@ -283,8 +284,12 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe setUsernameDelayed(); } if (Utils.isEmpty(username) && !isLoggedIn) { + binding.infoContainer.setVisibility(View.GONE); binding.privatePage1.setImageResource(R.drawable.ic_outline_info_24); binding.privatePage2.setText(R.string.no_acc); + final NestedCoordinatorLayout.LayoutParams layoutParams = (NestedCoordinatorLayout.LayoutParams) binding.privatePage.getLayoutParams(); + layoutParams.topMargin = 0; + binding.privatePage.setLayoutParams(layoutParams); binding.privatePage.setVisibility(View.VISIBLE); return; } @@ -302,10 +307,6 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe this.username = username; setUsernameDelayed(); fetchProfileDetails(); - // adds cookies to database for quick access - final DataBox.CookieModel cookieModel = Utils.dataBox.getCookie(uid); - if (Utils.dataBox.getCookieCount() == 0 || cookieModel == null || Utils.isEmpty(cookieModel.getUsername())) - Utils.dataBox.addUserCookie(new DataBox.CookieModel(uid, username, cookie)); }; boolean found = false; final DataBox.CookieModel cookieModel = Utils.dataBox.getCookie(uid); @@ -334,7 +335,9 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe && profileModel != null && userIdFromCookie != null && userIdFromCookie.equals(profileModel.getId()); - favMenuItem.setVisible(isSelf); + if (favMenuItem != null) { + favMenuItem.setVisible(isSelf); + } setProfileDetails(); }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); @@ -651,18 +654,12 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe }); }); binding.btnSaved.setOnClickListener(v -> { - // startActivity(new Intent(requireContext(), SavedViewerFragment.class) - // .putExtra(Constants.EXTRAS_INDEX, "$" + profileModel.getId()) - // .putExtra(Constants.EXTRAS_USER, "@" + profileModel.getUsername())); final NavDirections action = ProfileFragmentDirections.actionProfileFragmentToSavedViewerFragment(profileModel.getUsername(), profileModel.getId(), PostItemType.SAVED); NavHostFragment.findNavController(this).navigate(action); }); binding.btnLiked.setOnClickListener(v -> { - // startActivity(new Intent(requireContext(), SavedViewerFragment.class) - // .putExtra(Constants.EXTRAS_INDEX, "^" + profileModel.getId()) - // .putExtra(Constants.EXTRAS_USER, username)); final NavDirections action = ProfileFragmentDirections.actionProfileFragmentToSavedViewerFragment(profileModel.getUsername(), profileModel.getId(), PostItemType.LIKED); diff --git a/app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java b/app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java index 2ce0f50c..369ea7e9 100644 --- a/app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java @@ -1,57 +1,94 @@ package awais.instagrabber.fragments.settings; +import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.util.Log; +import android.view.View; +import android.widget.ArrayAdapter; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; +import androidx.fragment.app.FragmentActivity; import androidx.navigation.NavDirections; import androidx.navigation.fragment.NavHostFragment; import androidx.preference.Preference; import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceScreen; +import androidx.preference.PreferenceViewHolder; + +import java.util.Collections; +import java.util.List; import awais.instagrabber.BuildConfig; import awais.instagrabber.R; import awais.instagrabber.activities.Login; +import awais.instagrabber.adapters.AccountSwitcherListAdapter; +import awais.instagrabber.adapters.AccountSwitcherListAdapter.OnAccountClickListener; +import awais.instagrabber.databinding.PrefAccountSwitcherBinding; +import awais.instagrabber.repositories.responses.UserInfo; +import awais.instagrabber.services.ProfileService; +import awais.instagrabber.services.ServiceCallback; import awais.instagrabber.utils.Constants; +import awais.instagrabber.utils.DataBox; import awais.instagrabber.utils.FlavorTown; import awais.instagrabber.utils.Utils; +import static awais.instagrabber.adapters.AccountSwitcherListAdapter.OnAccountLongClickListener; import static awais.instagrabber.utils.Utils.settingsHelper; public class MorePreferencesFragment extends BasePreferencesFragment { private static final String TAG = "MorePreferencesFragment"; - - private final String cookie = settingsHelper.getString(Constants.COOKIE); + private AlertDialog accountSwitchDialog; + private DataBox.CookieModel tappedModel; + private ArrayAdapter adapter; @Override void setupPreferenceScreen(final PreferenceScreen screen) { - screen.addPreference(new MoreHeaderPreference(requireContext())); + final String cookie = settingsHelper.getString(Constants.COOKIE); + final boolean isLoggedIn = !Utils.isEmpty(cookie) && Utils.getUserIdFromCookie(cookie) != null; + // screen.addPreference(new MoreHeaderPreference(requireContext())); final PreferenceCategory accountCategory = new PreferenceCategory(requireContext()); accountCategory.setTitle("Account"); accountCategory.setIconSpaceReserved(false); screen.addPreference(accountCategory); - final boolean isLoggedIn = !Utils.isEmpty(cookie) && Utils.getUserIdFromCookie(cookie) != null; - screen.addPreference(getPreference( - isLoggedIn ? R.string.relogin : R.string.login, - isLoggedIn ? R.string.relogin_summary : -1, - -1, - preference -> { - startActivityForResult(new Intent(requireContext(), Login.class), Constants.LOGIN_RESULT_CODE); - return true; - })); + // To re-login, user can just add the same account back from account switcher dialog + // accountCategory.addPreference(getPreference( + // isLoggedIn ? R.string.relogin : R.string.login, + // isLoggedIn ? R.string.relogin_summary : -1, + // -1, + // preference -> { + // startActivityForResult(new Intent(requireContext(), Login.class), Constants.LOGIN_RESULT_CODE); + // return true; + // })); if (isLoggedIn) { - screen.addPreference(getPreference(R.string.logout, -1, preference -> { - Utils.setupCookies("LOGOUT"); - shouldRecreate(); - Toast.makeText(requireContext(), R.string.logout_success, Toast.LENGTH_SHORT).show(); - settingsHelper.putString(Constants.COOKIE, ""); + accountCategory.addPreference(getAccountSwitcherPreference(cookie)); + accountCategory.addPreference(getPreference(R.string.logout, "Remove all accounts", -1, preference -> { + if (getContext() == null) return false; + new AlertDialog.Builder(getContext()) + .setTitle(R.string.logout) + .setMessage("This will remove all added accounts from the app!\n" + + "To remove just one account, long tap the account from the account switcher dialog.\n" + + "Do you want to continue?") + .setPositiveButton(R.string.yes, (dialog, which) -> { + Utils.setupCookies("LOGOUT"); + shouldRecreate(); + Toast.makeText(requireContext(), R.string.logout_success, Toast.LENGTH_SHORT).show(); + settingsHelper.putString(Constants.COOKIE, ""); + }) + .setNegativeButton(R.string.cancel, null) + .show(); + return true; + })); + } else { + // Need to show something to trigger login activity + accountCategory.addPreference(getPreference(R.string.add_account, R.drawable.ic_add, preference -> { + startActivityForResult(new Intent(getContext(), Login.class), Constants.LOGIN_RESULT_CODE); return true; })); } @@ -86,12 +123,131 @@ public class MorePreferencesFragment extends BasePreferencesFragment { if (data == null) return; final String cookie = data.getStringExtra("cookie"); Utils.setupCookies(cookie); - shouldRecreate(); - Toast.makeText(requireContext(), R.string.login_success_loading_cookies, Toast.LENGTH_SHORT).show(); settingsHelper.putString(Constants.COOKIE, cookie); + // No use as the timing of show is unreliable + // Toast.makeText(requireContext(), R.string.login_success_loading_cookies, Toast.LENGTH_SHORT).show(); + + // adds cookies to database for quick access + final String uid = Utils.getUserIdFromCookie(cookie); + final ProfileService profileService = ProfileService.getInstance(); + profileService.getUserInfo(uid, new ServiceCallback() { + @Override + public void onSuccess(final UserInfo result) { + // Log.d(TAG, "adding userInfo: " + result); + if (result != null) { + Utils.dataBox.addOrUpdateUser(uid, result.getUsername(), cookie, result.getFullName(), result.getProfilePicUrl()); + } + final FragmentActivity activity = getActivity(); + if (activity == null) return; + activity.recreate(); + } + + @Override + public void onFailure(final Throwable t) { + Log.e(TAG, "Error fetching user info", t); + } + }); } } + @NonNull + private AccountSwitcherPreference getAccountSwitcherPreference(final String cookie) { + final List allUsers = Utils.dataBox.getAllCookies(); + if (getContext() != null && allUsers != null) { + sortUserList(cookie, allUsers); + final OnAccountClickListener clickListener = (model, isCurrent) -> { + if (isCurrent) { + if (accountSwitchDialog == null) return; + accountSwitchDialog.dismiss(); + return; + } + tappedModel = model; + shouldRecreate(); + if (accountSwitchDialog == null) return; + accountSwitchDialog.dismiss(); + }; + final OnAccountLongClickListener longClickListener = (model, isCurrent) -> { + if (isCurrent) { + new AlertDialog.Builder(getContext()) + .setMessage(R.string.quick_access_cannot_delete_curr) + .setPositiveButton(R.string.ok, null) + .show(); + return true; + } + new AlertDialog.Builder(getContext()) + .setMessage(getString(R.string.quick_access_confirm_delete, model.getUsername())) + .setPositiveButton(R.string.yes, (dialog, which) -> { + Utils.dataBox.delUserCookie(model); + adapter.clear(); + final List users = Utils.dataBox.getAllCookies(); + if (users == null) return; + adapter.addAll(users); + }) + .setNegativeButton(R.string.cancel, null) + .show(); + accountSwitchDialog.dismiss(); + return true; + }; + adapter = new AccountSwitcherListAdapter( + getContext(), + R.layout.pref_account_switcher, + allUsers, + clickListener, + longClickListener + ); + accountSwitchDialog = new AlertDialog.Builder(getContext()) + .setTitle("Accounts") + .setNeutralButton("Add account", (dialog1, which) -> startActivityForResult( + new Intent(getContext(), Login.class), + Constants.LOGIN_RESULT_CODE)) + .setAdapter(adapter, null) + .create(); + accountSwitchDialog.setOnDismissListener(dialog -> { + if (tappedModel == null) return; + Utils.setupCookies(tappedModel.getCookie()); + settingsHelper.putString(Constants.COOKIE, tappedModel.getCookie()); + }); + } + final AlertDialog finalDialog = accountSwitchDialog; + return new AccountSwitcherPreference(requireContext(), cookie, v -> { + if (finalDialog == null) return; + finalDialog.show(); + }); + } + + /** + * Sort the user list by following logic: + *
    + *
  1. Keep currently active account at top. + *
  2. Check if any user does not have a full name. + *
  3. If all have full names, sort by full names. + *
  4. Otherwise, sort by the usernames + *
+ * + * @param cookie active cookie + * @param allUsers list of users + */ + private void sortUserList(final String cookie, final List allUsers) { + boolean sortByName = true; + for (final DataBox.CookieModel user : allUsers) { + if (Utils.isEmpty(user.getFullName())) { + sortByName = false; + break; + } + } + final boolean finalSortByName = sortByName; + Collections.sort(allUsers, (o1, o2) -> { + // keep current account at top + if (o1.getCookie().equals(cookie)) return -1; + if (finalSortByName) { + // sort by full name + return o1.getFullName().compareTo(o2.getFullName()); + } + // otherwise sort by username + return o1.getUsername().compareTo(o2.getUsername()); + }); + } + @NonNull private Preference getPreference(final int title, final int icon, @@ -139,4 +295,33 @@ public class MorePreferencesFragment extends BasePreferencesFragment { setSelectable(false); } } + + public static class AccountSwitcherPreference extends Preference { + + private final String cookie; + private final View.OnClickListener onClickListener; + + public AccountSwitcherPreference(final Context context, + final String cookie, + final View.OnClickListener onClickListener) { + super(context); + this.cookie = cookie; + this.onClickListener = onClickListener; + setLayoutResource(R.layout.pref_account_switcher); + } + + @SuppressLint("SetTextI18n") + @Override + public void onBindViewHolder(final PreferenceViewHolder holder) { + final View root = holder.itemView; + if (onClickListener != null) root.setOnClickListener(onClickListener); + final PrefAccountSwitcherBinding binding = PrefAccountSwitcherBinding.bind(root); + final String uid = Utils.getUserIdFromCookie(cookie); + final DataBox.CookieModel user = Utils.dataBox.getCookie(uid); + if (user == null) return; + binding.fullName.setText(user.getFullName()); + binding.username.setText("@" + user.getUsername()); + binding.profilePic.setImageURI(user.getProfilePic()); + } + } } diff --git a/app/src/main/java/awais/instagrabber/fragments/settings/SettingsPreferencesFragment.java b/app/src/main/java/awais/instagrabber/fragments/settings/SettingsPreferencesFragment.java index 6dbbe5d5..170e5834 100644 --- a/app/src/main/java/awais/instagrabber/fragments/settings/SettingsPreferencesFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/settings/SettingsPreferencesFragment.java @@ -37,13 +37,6 @@ public class SettingsPreferencesFragment extends BasePreferencesFragment { private static AppCompatTextView customPathTextView; private boolean isLoggedIn; - @Override - public void onCreate(@Nullable final Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setHasOptionsMenu(true); - - } - @Override void setupPreferenceScreen(final PreferenceScreen screen) { final String cookie = settingsHelper.getString(Constants.COOKIE); diff --git a/app/src/main/java/awais/instagrabber/repositories/ProfileRepository.java b/app/src/main/java/awais/instagrabber/repositories/ProfileRepository.java new file mode 100644 index 00000000..62245dda --- /dev/null +++ b/app/src/main/java/awais/instagrabber/repositories/ProfileRepository.java @@ -0,0 +1,11 @@ +package awais.instagrabber.repositories; + +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.Path; + +public interface ProfileRepository { + + @GET("api/v1/users/{uid}/info/") + Call getUserInfo(@Path("uid") final String uid); +} diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/UserInfo.java b/app/src/main/java/awais/instagrabber/repositories/responses/UserInfo.java new file mode 100644 index 00000000..4cca9b69 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/repositories/responses/UserInfo.java @@ -0,0 +1,41 @@ +package awais.instagrabber.repositories.responses; + +public class UserInfo { + private final String pk; + private final String username; + private final String fullName; + private final String profilePicUrl; + + public UserInfo(final String pk, final String username, final String fullName, final String profilePicUrl) { + this.pk = pk; + this.username = username; + this.fullName = fullName; + this.profilePicUrl = profilePicUrl; + } + + public String getPk() { + return pk; + } + + public String getUsername() { + return username; + } + + public String getFullName() { + return fullName; + } + + public String getProfilePicUrl() { + return profilePicUrl; + } + + @Override + public String toString() { + return "UserInfo{" + + "uid='" + pk + '\'' + + ", username='" + username + '\'' + + ", fullName='" + fullName + '\'' + + ", profilePicUrl='" + profilePicUrl + '\'' + + '}'; + } +} diff --git a/app/src/main/java/awais/instagrabber/services/AddCookiesInterceptor.java b/app/src/main/java/awais/instagrabber/services/AddCookiesInterceptor.java index 052e4f6c..38709e09 100644 --- a/app/src/main/java/awais/instagrabber/services/AddCookiesInterceptor.java +++ b/app/src/main/java/awais/instagrabber/services/AddCookiesInterceptor.java @@ -17,7 +17,8 @@ public class AddCookiesInterceptor implements Interceptor { final Request request = chain.request(); final Request.Builder builder = request.newBuilder(); final String cookie = Utils.settingsHelper.getString(Constants.COOKIE); - builder.addHeader("Cookie", cookie); + builder.addHeader("Cookie", cookie) + .addHeader("User-Agent", Constants.I_USER_AGENT); final Request updatedRequest = builder.build(); return chain.proceed(updatedRequest); } diff --git a/app/src/main/java/awais/instagrabber/services/FeedService.java b/app/src/main/java/awais/instagrabber/services/FeedService.java deleted file mode 100644 index 9f65b2f0..00000000 --- a/app/src/main/java/awais/instagrabber/services/FeedService.java +++ /dev/null @@ -1,4 +0,0 @@ -package awais.instagrabber.services; - -public interface FeedService { -} diff --git a/app/src/main/java/awais/instagrabber/services/FriendshipService.java b/app/src/main/java/awais/instagrabber/services/FriendshipService.java index d1fac3dd..c3b500d7 100644 --- a/app/src/main/java/awais/instagrabber/services/FriendshipService.java +++ b/app/src/main/java/awais/instagrabber/services/FriendshipService.java @@ -17,7 +17,7 @@ import retrofit2.Response; import retrofit2.Retrofit; public class FriendshipService extends BaseService { - private static final String TAG = "ProfileService"; + private static final String TAG = "FriendshipService"; private final FriendshipRepository repository; diff --git a/app/src/main/java/awais/instagrabber/services/LoggingInterceptor.java b/app/src/main/java/awais/instagrabber/services/LoggingInterceptor.java index 15fce4c9..d4f9ca09 100644 --- a/app/src/main/java/awais/instagrabber/services/LoggingInterceptor.java +++ b/app/src/main/java/awais/instagrabber/services/LoggingInterceptor.java @@ -18,18 +18,22 @@ class LoggingInterceptor implements Interceptor { @NonNull @Override public Response intercept(Interceptor.Chain chain) throws IOException { - Request request = chain.request(); + final Request request = chain.request(); long t1 = System.nanoTime(); Log.i(TAG, String.format("Sending request %s on %s%n%s", request.url(), chain.connection(), request.headers())); - Response response = chain.proceed(request); + final Response response = chain.proceed(request); long t2 = System.nanoTime(); Log.i(TAG, String.format("Received response for %s in %.1fms%n%s", response.request().url(), (t2 - t1) / 1e6d, response.headers())); - MediaType contentType = response.body().contentType(); - String content = response.body().string(); - Log.d("OkHttp", content); - - ResponseBody wrappedBody = ResponseBody.create(contentType, content); + final ResponseBody body = response.body(); + MediaType contentType = null; + String content = ""; + if (body != null) { + contentType = body.contentType(); + content = body.string(); + Log.d(TAG, content); + } + final ResponseBody wrappedBody = ResponseBody.create(contentType, content); return response.newBuilder() .body(wrappedBody) .build(); diff --git a/app/src/main/java/awais/instagrabber/services/ProfileService.java b/app/src/main/java/awais/instagrabber/services/ProfileService.java new file mode 100644 index 00000000..5352b6db --- /dev/null +++ b/app/src/main/java/awais/instagrabber/services/ProfileService.java @@ -0,0 +1,69 @@ +package awais.instagrabber.services; + +import android.util.Log; + +import androidx.annotation.NonNull; + +import org.json.JSONException; +import org.json.JSONObject; + +import awais.instagrabber.repositories.ProfileRepository; +import awais.instagrabber.repositories.responses.UserInfo; +import awais.instagrabber.utils.Constants; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; +import retrofit2.Retrofit; + +public class ProfileService extends BaseService { + private static final String TAG = "ProfileService"; + + private final ProfileRepository repository; + + private static ProfileService instance; + + private ProfileService() { + final Retrofit retrofit = getRetrofitBuilder() + .baseUrl("https://i.instagram.com") + .build(); + repository = retrofit.create(ProfileRepository.class); + } + + public static ProfileService getInstance() { + if (instance == null) { + instance = new ProfileService(); + } + return instance; + } + + public void getUserInfo(final String uid, final ServiceCallback callback) { + final Call request = repository.getUserInfo(uid); + request.enqueue(new Callback() { + @Override + public void onResponse(@NonNull final Call call, @NonNull final Response response) { + final String body = response.body(); + if (body == null) return; + try { + final JSONObject jsonObject = new JSONObject(body); + final JSONObject user = jsonObject.optJSONObject(Constants.EXTRAS_USER); + if (user == null) return; + // Log.d(TAG, "user: " + user.toString()); + final UserInfo userInfo = new UserInfo( + uid, + user.getString(Constants.EXTRAS_USERNAME), + user.optString("full_name"), + user.optString("profile_pic_url") + ); + callback.onSuccess(userInfo); + } catch (JSONException e) { + Log.e(TAG, "Error parsing json", e); + } + } + + @Override + public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { + callback.onFailure(t); + } + }); + } +} diff --git a/app/src/main/java/awais/instagrabber/utils/DataBox.java b/app/src/main/java/awais/instagrabber/utils/DataBox.java index cd37358b..93ed8153 100755 --- a/app/src/main/java/awais/instagrabber/utils/DataBox.java +++ b/app/src/main/java/awais/instagrabber/utils/DataBox.java @@ -10,6 +10,7 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.util.ObjectsCompat; import java.util.ArrayList; @@ -19,38 +20,60 @@ import awaisomereport.LogCollector; import static awais.instagrabber.utils.Utils.logCollector; public final class DataBox extends SQLiteOpenHelper { + private static final String TAG = "DataBox"; + private static DataBox sInstance; - private final static int VERSION = 1; + + private final static int VERSION = 2; private final static String TABLE_COOKIES = "cookies"; private final static String TABLE_FAVORITES = "favorites"; private final static String KEY_DATE_ADDED = "date_added"; private final static String KEY_QUERY_TEXT = "query_text"; private final static String KEY_QUERY_DISPLAY = "query_display"; + + private final static String KEY_ID = "id"; private final static String KEY_USERNAME = Constants.EXTRAS_USERNAME; private final static String KEY_COOKIE = "cookie"; private final static String KEY_UID = "uid"; - private Context c; + private final static String KEY_FULL_NAME = "full_name"; + private final static String KEY_PROFILE_PIC = "profile_pic"; + + private final Context c; public static synchronized DataBox getInstance(final Context context) { if (sInstance == null) sInstance = new DataBox(context.getApplicationContext()); return sInstance; } - public DataBox(@Nullable final Context context) { + private DataBox(@Nullable final Context context) { super(context, "cookiebox.db", null, VERSION); c = context; } @Override public void onCreate(@NonNull final SQLiteDatabase db) { - db.execSQL("CREATE TABLE cookies (id INTEGER PRIMARY KEY, uid TEXT, username TEXT, cookie TEXT)"); + Log.i(TAG, "Creating tables..."); + db.execSQL("CREATE TABLE " + TABLE_COOKIES + " (" + + KEY_ID + " INTEGER PRIMARY KEY," + + KEY_UID + " TEXT," + + KEY_USERNAME + " TEXT," + + KEY_COOKIE + " TEXT," + + KEY_FULL_NAME + " TEXT," + + KEY_PROFILE_PIC + " TEXT)"); db.execSQL("CREATE TABLE favorites (id INTEGER PRIMARY KEY, query_text TEXT, date_added INTEGER, query_display TEXT)"); + Log.i(TAG, "Tables created!"); } @Override - public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) { } + public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) { + Log.i(TAG, String.format("Updating DB from v%d to v%d", oldVersion, newVersion)); + if (oldVersion == 1) { + db.execSQL("ALTER TABLE " + TABLE_COOKIES + " ADD " + KEY_FULL_NAME + " TEXT"); + db.execSQL("ALTER TABLE " + TABLE_COOKIES + " ADD " + KEY_PROFILE_PIC + " TEXT"); + } + Log.i(TAG, String.format("DB update from v%d to v%d completed!", oldVersion, newVersion)); + } - ///////////////////////////////////////// YOUR FAVORITES! HERE ///////////////////////////////////////// public final void addFavorite(@NonNull final FavoriteModel favoriteModel) { final String query = favoriteModel.getQuery(); final String display = favoriteModel.getDisplayName(); @@ -87,16 +110,16 @@ public final class DataBox extends SQLiteOpenHelper { db.beginTransaction(); try { final int rowsDeleted = db.delete(TABLE_FAVORITES, "query_text=? AND date_added=?", - new String[]{query, Long.toString(favoriteModel.getDate())}); + new String[]{query, Long.toString(favoriteModel.getDate())}); final int rowsDeletedTwo = db.delete(TABLE_FAVORITES, "query_text=? AND date_added=?", - new String[]{query.replaceAll("@", ""), Long.toString(favoriteModel.getDate())}); + new String[]{query.replaceAll("@", ""), Long.toString(favoriteModel.getDate())}); if (rowsDeleted > 0 || rowsDeletedTwo > 0) db.setTransactionSuccessful(); } catch (final Exception e) { if (logCollector != null) logCollector.appendException(e, LogCollector.LogFile.DATA_BOX_FAVORITES, "delFavorite"); - if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); + if (BuildConfig.DEBUG) Log.e(TAG, "Error", e); } finally { db.endTransaction(); } @@ -117,12 +140,13 @@ public final class DataBox extends SQLiteOpenHelper { do { tempFav = new FavoriteModel( (cursor.getString(0).charAt(0) == '@' || cursor.getString(0).charAt(0) == '#' || cursor.getString(0).contains("/")) - ? cursor.getString(0) - : "@" + cursor.getString(0), // query text + ? cursor.getString(0) + : "@" + cursor.getString(0), // query text cursor.getLong(1), // date added - cursor.getString(2) == null ? (cursor.getString(0).charAt(0) == '@' || cursor.getString(0).charAt(0) == '#' || cursor.getString(0).contains("/")) - ? cursor.getString(0) - : "@" + cursor.getString(0) : cursor.getString(2) // display + cursor.getString(2) == null ? (cursor.getString(0).charAt(0) == '@' || cursor.getString(0).charAt(0) == '#' || cursor + .getString(0).contains("/")) + ? cursor.getString(0) + : "@" + cursor.getString(0) : cursor.getString(2) // display ); if (cursor.getString(2) == null) { try { @@ -154,7 +178,7 @@ public final class DataBox extends SQLiteOpenHelper { if (logCollector != null) logCollector.appendException(e, LogCollector.LogFile.DATA_BOX_FAVORITES, "migrate"); Toast.makeText(c, "DB migration failed, contact maintainer.", Toast.LENGTH_SHORT).show(); - if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); + if (BuildConfig.DEBUG) Log.e(TAG, "", e); } } @@ -162,11 +186,9 @@ public final class DataBox extends SQLiteOpenHelper { } public final String getFavorite(@NonNull final String query) { - ArrayList favorites = null; - try (final SQLiteDatabase db = getReadableDatabase(); final Cursor cursor = db.rawQuery("SELECT query_text, date_added FROM favorites WHERE " - +KEY_QUERY_TEXT+"='"+query+"' ORDER BY date_added DESC", null)) { + + KEY_QUERY_TEXT + "='" + query + "' ORDER BY date_added DESC", null)) { if (cursor != null && cursor.moveToFirst()) { return cursor.getString(0) + "/" + String.valueOf(cursor.getLong(1)); } @@ -174,31 +196,33 @@ public final class DataBox extends SQLiteOpenHelper { return null; } - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////// YOUR COOKIES FOR COOKIE MONSTER ARE HERE ///////////////////////////////////// - public final void addUserCookie(@NonNull final CookieModel cookieModel) { - final String cookieModelUid = cookieModel.getUid(); - if (!Utils.isEmpty(cookieModelUid)) { - try (final SQLiteDatabase db = getWritableDatabase()) { - db.beginTransaction(); - try { - final ContentValues values = new ContentValues(); - values.put(KEY_USERNAME, cookieModel.getUsername()); - values.put(KEY_COOKIE, cookieModel.getCookie()); - values.put(KEY_UID, cookieModelUid); + public final void addOrUpdateUser(final String uid, + final String username, + final String cookie, + final String fullName, + final String profilePicUrl) { + if (Utils.isEmpty(uid)) return; + try (final SQLiteDatabase db = getWritableDatabase()) { + db.beginTransaction(); + try { + final ContentValues values = new ContentValues(); + values.put(KEY_USERNAME, username); + values.put(KEY_COOKIE, cookie); + values.put(KEY_UID, uid); + values.put(KEY_FULL_NAME, fullName); + values.put(KEY_PROFILE_PIC, profilePicUrl); - final int rows = db.update(TABLE_COOKIES, values, KEY_UID + "=?", new String[]{cookieModelUid}); + final int rows = db.update(TABLE_COOKIES, values, KEY_UID + "=?", new String[]{uid}); - if (rows != 1) - db.insertOrThrow(TABLE_COOKIES, null, values); + if (rows != 1) + db.insertOrThrow(TABLE_COOKIES, null, values); - db.setTransactionSuccessful(); - } catch (final Exception e) { - if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); - } finally { - db.endTransaction(); - } + db.setTransactionSuccessful(); + } catch (final Exception e) { + if (BuildConfig.DEBUG) Log.e(TAG, "Error", e); + } finally { + db.endTransaction(); } } } @@ -210,7 +234,7 @@ public final class DataBox extends SQLiteOpenHelper { db.beginTransaction(); try { final int rowsDeleted = db.delete(TABLE_COOKIES, KEY_UID + "=? AND " + KEY_USERNAME + "=? AND " + KEY_COOKIE + "=?", - new String[]{cookieModelUid, cookieModel.getUsername(), cookieModel.getCookie()}); + new String[]{cookieModelUid, cookieModel.getUsername(), cookieModel.getCookie()}); if (rowsDeleted > 0) db.setTransactionSuccessful(); } catch (final Exception e) { @@ -222,6 +246,21 @@ public final class DataBox extends SQLiteOpenHelper { } } + public final synchronized void deleteAllUserCookies() { + try (final SQLiteDatabase db = getWritableDatabase()) { + db.beginTransaction(); + try { + final int rowsDeleted = db.delete(TABLE_COOKIES, null, null); + + if (rowsDeleted > 0) db.setTransactionSuccessful(); + } catch (final Exception e) { + if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); + } finally { + db.endTransaction(); + } + } + } + public final int getCookieCount() { int cookieCount = 0; try (final SQLiteDatabase db = getReadableDatabase(); @@ -235,12 +274,24 @@ public final class DataBox extends SQLiteOpenHelper { public final CookieModel getCookie(final String uid) { CookieModel cookie = null; try (final SQLiteDatabase db = getReadableDatabase(); - final Cursor cursor = db.rawQuery("SELECT uid, username, cookie FROM cookies WHERE uid = ?", new String[]{uid})) { + final Cursor cursor = db.rawQuery( + "SELECT " + + KEY_UID + "," + + KEY_USERNAME + "," + + KEY_COOKIE + "," + + KEY_FULL_NAME + "," + + KEY_PROFILE_PIC + + " FROM " + TABLE_COOKIES + + " WHERE " + KEY_UID + " = ?", + new String[]{uid}) + ) { if (cursor != null && cursor.moveToFirst()) cookie = new CookieModel( - cursor.getString(0), // uid - cursor.getString(1), // username - cursor.getString(2) // cookie + cursor.getString(cursor.getColumnIndex(KEY_UID)), + cursor.getString(cursor.getColumnIndex(KEY_USERNAME)), + cursor.getString(cursor.getColumnIndex(KEY_COOKIE)), + cursor.getString(cursor.getColumnIndex(KEY_FULL_NAME)), + cursor.getString(cursor.getColumnIndex(KEY_PROFILE_PIC)) ); } return cookie; @@ -251,14 +302,24 @@ public final class DataBox extends SQLiteOpenHelper { ArrayList cookies = null; try (final SQLiteDatabase db = getReadableDatabase(); - final Cursor cursor = db.rawQuery("SELECT uid, username, cookie FROM cookies", null)) { + final Cursor cursor = db.rawQuery( + "SELECT " + + KEY_UID + "," + + KEY_USERNAME + "," + + KEY_COOKIE + "," + + KEY_FULL_NAME + "," + + KEY_PROFILE_PIC + + " FROM " + TABLE_COOKIES, null) + ) { if (cursor != null && cursor.moveToFirst()) { cookies = new ArrayList<>(); do { cookies.add(new CookieModel( - cursor.getString(0), // uid - cursor.getString(1), // username - cursor.getString(2) // cookie + cursor.getString(cursor.getColumnIndex(KEY_UID)), + cursor.getString(cursor.getColumnIndex(KEY_USERNAME)), + cursor.getString(cursor.getColumnIndex(KEY_COOKIE)), + cursor.getString(cursor.getColumnIndex(KEY_FULL_NAME)), + cursor.getString(cursor.getColumnIndex(KEY_PROFILE_PIC)) )); } while (cursor.moveToNext()); } @@ -266,16 +327,25 @@ public final class DataBox extends SQLiteOpenHelper { return cookies; } - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static class CookieModel { - private final String uid, username, cookie; + private final String uid; + private final String username; + private final String cookie; + private final String fullName; + private final String profilePic; private boolean selected; - public CookieModel(final String uid, final String username, final String cookie) { + public CookieModel(final String uid, + final String username, + final String cookie, + final String fullName, + final String profilePic) { this.uid = uid; this.username = username; this.cookie = cookie; + this.fullName = fullName; + this.profilePic = profilePic; } public String getUid() { @@ -290,6 +360,14 @@ public final class DataBox extends SQLiteOpenHelper { return cookie; } + public String getFullName() { + return fullName; + } + + public String getProfilePic() { + return profilePic; + } + public boolean isSelected() { return selected; } @@ -298,6 +376,21 @@ public final class DataBox extends SQLiteOpenHelper { this.selected = selected; } + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final CookieModel that = (CookieModel) o; + return ObjectsCompat.equals(uid, that.uid) && + ObjectsCompat.equals(username, that.username) && + ObjectsCompat.equals(cookie, that.cookie); + } + + @Override + public int hashCode() { + return ObjectsCompat.hash(uid, username, cookie); + } + @NonNull @Override public String toString() { diff --git a/app/src/main/java/awais/instagrabber/utils/ExportImportUtils.java b/app/src/main/java/awais/instagrabber/utils/ExportImportUtils.java index 0ba66f32..73a42f41 100755 --- a/app/src/main/java/awais/instagrabber/utils/ExportImportUtils.java +++ b/app/src/main/java/awais/instagrabber/utils/ExportImportUtils.java @@ -97,28 +97,30 @@ public final class ExportImportUtils { final AppCompatEditText editText = new AppCompatEditText(context); editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(32)}); editText.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD); - new AlertDialog.Builder(context).setView(editText).setTitle(R.string.password).setPositiveButton(R.string.confirm, (dialog, which) -> { - final CharSequence text = editText.getText(); - if (!Utils.isEmpty(text)) { - try { - final byte[] passwordBytes = text.toString().getBytes(); - final byte[] bytes = new byte[32]; - System.arraycopy(passwordBytes, 0, bytes, 0, Math.min(passwordBytes.length, 32)); - saveToSettings(new String(PasswordUtils.dec(builder.toString(), bytes)), flags, fetchListener); - } catch (final Exception e) { - if (fetchListener != null) fetchListener.onResult(false); - if (logCollector != null) - logCollector.appendException(e, LogFile.UTILS_IMPORT, "Import::pass"); - if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); - } + new AlertDialog.Builder(context).setView(editText).setTitle(R.string.password) + .setPositiveButton(R.string.confirm, (dialog, which) -> { + final CharSequence text = editText.getText(); + if (!Utils.isEmpty(text)) { + try { + final byte[] passwordBytes = text.toString().getBytes(); + final byte[] bytes = new byte[32]; + System.arraycopy(passwordBytes, 0, bytes, 0, Math.min(passwordBytes.length, 32)); + saveToSettings(new String(PasswordUtils.dec(builder.toString(), bytes)), flags, + fetchListener); + } catch (final Exception e) { + if (fetchListener != null) fetchListener.onResult(false); + if (logCollector != null) + logCollector.appendException(e, LogFile.UTILS_IMPORT, "Import::pass"); + if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); + } - } else - Toast.makeText(context, R.string.dialog_export_err_password_empty, Toast.LENGTH_SHORT).show(); - }).show(); + } else + Toast.makeText(context, R.string.dialog_export_err_password_empty, Toast.LENGTH_SHORT).show(); + }).show(); } else if (configType == 'Z') { saveToSettings(new String(Base64.decode(builder.toString(), Base64.DEFAULT | Base64.NO_PADDING | Base64.NO_WRAP)), - flags, fetchListener); + flags, fetchListener); } else { Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); @@ -156,8 +158,12 @@ public final class ExportImportUtils { final int cookiesLen = cookies.length(); for (int i = 0; i < cookiesLen; ++i) { final JSONObject cookieObject = cookies.getJSONObject(i); - Utils.dataBox.addUserCookie(new DataBox.CookieModel(cookieObject.getString("i"), - cookieObject.getString("u"), cookieObject.getString("c"))); + // final DataBox.CookieModel cookieModel = new DataBox.CookieModel(cookieObject.getString("i"), + // cookieObject.getString("u"), + // cookieObject.getString("c"), + // fullName, + // profilePic); + // Utils.dataBox.addOrUpdateUser(cookieModel.getUid(), cookieModel.getUserInfo(), cookieModel.getCookie()); } } @@ -167,7 +173,8 @@ public final class ExportImportUtils { for (int i = 0; i < favsLen; ++i) { final JSONObject favsObject = favs.getJSONObject(i); Utils.dataBox.addFavorite(new DataBox.FavoriteModel(favsObject.getString("q"), - favsObject.getLong("d"), favsObject.has("s") ? favsObject.getString("s") : favsObject.getString("q"))); + favsObject.getLong("d"), + favsObject.has("s") ? favsObject.getString("s") : favsObject.getString("q"))); } } diff --git a/app/src/main/java/awais/instagrabber/utils/NavigationExtensions.java b/app/src/main/java/awais/instagrabber/utils/NavigationExtensions.java index 8a166b4d..87c225a1 100644 --- a/app/src/main/java/awais/instagrabber/utils/NavigationExtensions.java +++ b/app/src/main/java/awais/instagrabber/utils/NavigationExtensions.java @@ -20,6 +20,7 @@ import java.util.List; import awais.instagrabber.R; public class NavigationExtensions { + // private static final String TAG = "NavigationExtensions"; @NonNull public static LiveData setupWithNavController(@NonNull final BottomNavigationView bottomNavigationView, @@ -48,7 +49,6 @@ public class NavigationExtensions { detachNavHostFragment(fragmentManager, navHostFragment); } } - final String[] selectedItemTag = {graphIdToTagMap.get(bottomNavigationView.getSelectedItemId())}; final String firstFragmentTag = graphIdToTagMap.get(firstFragmentGraphId); final boolean[] isOnFirstFragment = {selectedItemTag[0] != null && selectedItemTag[0].equals(firstFragmentTag)}; @@ -61,7 +61,8 @@ public class NavigationExtensions { fragmentManager.popBackStack(firstFragmentTag, FragmentManager.POP_BACK_STACK_INCLUSIVE); Fragment fragment = fragmentManager.findFragmentByTag(newlySelectedItemTag); if (fragment == null) { - throw new RuntimeException("null cannot be cast to non-null NavHostFragment"); + return false; + // throw new RuntimeException("null cannot be cast to non-null NavHostFragment"); } final NavHostFragment selectedFragment = (NavHostFragment) fragment; if (!firstFragmentTag.equals(newlySelectedItemTag)) { @@ -158,7 +159,8 @@ public class NavigationExtensions { final String newlySelectedItemTag = graphIdToTagMap.get(item.getItemId()); final Fragment fragmentByTag = fragmentManager.findFragmentByTag(newlySelectedItemTag); if (fragmentByTag == null) { - throw new NullPointerException("null cannot be cast to non-null type NavHostFragment"); + return; + // throw new NullPointerException("null cannot be cast to non-null type NavHostFragment"); } final NavHostFragment selectedFragment = (NavHostFragment) fragmentByTag; final NavController navController = selectedFragment.getNavController(); diff --git a/app/src/main/java/awais/instagrabber/utils/Utils.java b/app/src/main/java/awais/instagrabber/utils/Utils.java index f8f30ac7..52bb5ba7 100755 --- a/app/src/main/java/awais/instagrabber/utils/Utils.java +++ b/app/src/main/java/awais/instagrabber/utils/Utils.java @@ -133,6 +133,7 @@ public final class Utils { } if (cookieRaw.equals("LOGOUT")) { cookieStore.removeAll(); + dataBox.deleteAllUserCookies(); return; } try { diff --git a/app/src/main/res/drawable/ic_arrow_drop_down_24.xml b/app/src/main/res/drawable/ic_arrow_drop_down_24.xml new file mode 100644 index 00000000..ce583469 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_drop_down_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_check_24.xml b/app/src/main/res/drawable/ic_check_24.xml new file mode 100644 index 00000000..0432fa69 --- /dev/null +++ b/app/src/main/res/drawable/ic_check_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/pref_account_switcher.xml b/app/src/main/res/layout/pref_account_switcher.xml new file mode 100644 index 00000000..4eaf692c --- /dev/null +++ b/app/src/main/res/layout/pref_account_switcher.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 62f921fc..3d8dca86 100755 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -109,7 +109,6 @@ @navigation/profile_nav_graph - @navigation/discover_nav_graph @navigation/more_nav_graph diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 1f9d9ed4..65515f29 100755 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -4,6 +4,7 @@ @dimen/profile_picture_size 90dp + 40dp 48dp 80dp 70dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e647a454..a595cfd1 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -89,7 +89,7 @@ Mentions This Account is Private You won\'t be able to access posts after unfollowing! Are you sure? - You can log in via Settings on the bottom-right corner. Or, you can view public accounts without login! + You can log in via More -> Account on the bottom-right corner or you can view public accounts without login! You can swipe left/right for explore/feed, or search something below! This Account has No Posts No Such Posts! @@ -258,4 +258,5 @@ Theme Downloads Locale + Add account \ No newline at end of file