1
0
mirror of https://github.com/KokaKiwi/BarInsta synced 2024-11-22 06:37:30 +00:00

Add account switcher in MorePreferencesFragment

This commit is contained in:
Ammar Githam 2020-09-10 19:39:10 +09:00
parent 3e7097ff6d
commit 00762c85e6
24 changed files with 760 additions and 154 deletions

View File

@ -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) {
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) {
final String mainCookie = Utils.getCookie(webViewUrl);
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;
}
if (v == loginBinding.cookies) {
final String mainCookie = Utils.getCookie(webViewUrl);
if (Utils.isEmpty(mainCookie) || !mainCookie.contains("; ds_user_id=")) {
Toast.makeText(this, R.string.login_error_loading_cookies, Toast.LENGTH_SHORT).show();
return;
}
returnCookieResult(mainCookie);
}
}
@ -84,7 +88,8 @@ 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"
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);

View File

@ -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

View File

@ -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<DataBox.CookieModel> {
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<DataBox.CookieModel> 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);
}
}
}

View File

@ -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) -> {
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).setMessage(getString(R.string.quick_access_confirm_delete,
cookieModel.getUsername())).show();
.setNegativeButton(R.string.no, null)
.show();
}
return true;

View File

@ -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());
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);

View File

@ -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<DataBox.CookieModel> 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 -> {
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,10 +123,129 @@ 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<UserInfo>() {
@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<DataBox.CookieModel> 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<DataBox.CookieModel> 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:
* <ol>
* <li>Keep currently active account at top.
* <li>Check if any user does not have a full name.
* <li>If all have full names, sort by full names.
* <li>Otherwise, sort by the usernames
* </ol>
*
* @param cookie active cookie
* @param allUsers list of users
*/
private void sortUserList(final String cookie, final List<DataBox.CookieModel> 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
@ -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());
}
}
}

View File

@ -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);

View File

@ -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<String> getUserInfo(@Path("uid") final String uid);
}

View File

@ -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 + '\'' +
'}';
}
}

View File

@ -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);
}

View File

@ -1,4 +0,0 @@
package awais.instagrabber.services;
public interface FeedService {
}

View File

@ -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;

View File

@ -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();

View File

@ -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<UserInfo> callback) {
final Call<String> request = repository.getUserInfo(uid);
request.enqueue(new Callback<String>() {
@Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> 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<String> call, @NonNull final Throwable t) {
callback.onFailure(t);
}
});
}
}

View File

@ -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();
@ -96,7 +119,7 @@ public final class DataBox extends SQLiteOpenHelper {
} 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();
}
@ -120,7 +143,8 @@ public final class DataBox extends SQLiteOpenHelper {
? 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(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
);
@ -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<FavoriteModel> 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,34 +196,36 @@ 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)) {
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, cookieModel.getUsername());
values.put(KEY_COOKIE, cookieModel.getCookie());
values.put(KEY_UID, cookieModelUid);
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);
db.setTransactionSuccessful();
} catch (final Exception e) {
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
if (BuildConfig.DEBUG) Log.e(TAG, "Error", e);
} finally {
db.endTransaction();
}
}
}
}
public final synchronized void delUserCookie(@NonNull final CookieModel cookieModel) {
final String cookieModelUid = cookieModel.getUid();
@ -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<CookieModel> 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() {

View File

@ -97,14 +97,16 @@ 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) -> {
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);
saveToSettings(new String(PasswordUtils.dec(builder.toString(), bytes)), flags,
fetchListener);
} catch (final Exception e) {
if (fetchListener != null) fetchListener.onResult(false);
if (logCollector != null)
@ -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")));
}
}

View File

@ -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<NavController> 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();

View File

@ -133,6 +133,7 @@ public final class Utils {
}
if (cookieRaw.equals("LOGOUT")) {
cookieStore.removeAll();
dataBox.deleteAllUserCookies();
return;
}
try {

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M7,10l5,5 5,-5z"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
</vector>

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:padding="16dp">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/profile_pic"
android:layout_width="@dimen/image_size_40"
android:layout_height="@dimen/image_size_40"
android:scaleType="centerCrop"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:roundAsCircle="true" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/full_name"
android:layout_width="0dp"
android:layout_height="0dp"
android:gravity="center_vertical"
android:paddingStart="16dp"
android:paddingLeft="16dp"
android:paddingEnd="16dp"
android:paddingRight="16dp"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@id/username"
app:layout_constraintEnd_toStartOf="@id/arrow_down"
app:layout_constraintStart_toEndOf="@id/profile_pic"
app:layout_constraintTop_toTopOf="parent"
tools:text="full name" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/username"
android:layout_width="0dp"
android:layout_height="0dp"
android:gravity="center_vertical"
android:paddingStart="16dp"
android:paddingLeft="16dp"
android:paddingEnd="16dp"
android:paddingRight="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/arrow_down"
app:layout_constraintStart_toEndOf="@id/profile_pic"
app:layout_constraintTop_toBottomOf="@id/full_name"
tools:text="\@username" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/arrow_down"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/full_name"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_arrow_drop_down_24"
app:tint="?colorAccent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -109,7 +109,6 @@
</string-array>
<array name="logged_out_main_nav_ids">
<item>@navigation/profile_nav_graph</item>
<item>@navigation/discover_nav_graph</item>
<item>@navigation/more_nav_graph</item>
</array>
</resources>

View File

@ -4,6 +4,7 @@
<dimen name="private_page_margins">@dimen/profile_picture_size</dimen>
<dimen name="profile_picture_size">90dp</dimen>
<dimen name="image_size_40">40dp</dimen>
<dimen name="feed_profile_size">48dp</dimen>
<dimen name="slider_item_size">80dp</dimen>
<dimen name="highlight_size">70dp</dimen>

View File

@ -89,7 +89,7 @@
<string name="story_mentions">Mentions</string>
<string name="priv_acc">This Account is Private</string>
<string name="priv_acc_confirm">You won\'t be able to access posts after unfollowing! Are you sure?</string>
<string name="no_acc">You can log in via Settings on the bottom-right corner. Or, you can view public accounts without login!</string>
<string name="no_acc">You can log in via More -> Account on the bottom-right corner or you can view public accounts without login!</string>
<string name="no_acc_logged_in">You can swipe left/right for explore/feed, or search something below!</string>
<string name="empty_acc">This Account has No Posts</string>
<string name="empty_list">No Such Posts!</string>
@ -258,4 +258,5 @@
<string name="pref_category_theme">Theme</string>
<string name="pref_category_downloads">Downloads</string>
<string name="pref_category_locale">Locale</string>
<string name="add_account">Add account</string>
</resources>