mirror of
https://codeberg.org/gitnex/GitNex.git
synced 2024-12-16 15:48:13 +08:00
Update user avatar (#1349)
Closes #552 Reviewed-on: https://codeberg.org/gitnex/GitNex/pulls/1349 Co-authored-by: M M Arif <mmarif@swatian.com> Co-committed-by: M M Arif <mmarif@swatian.com>
This commit is contained in:
parent
7c435e4f34
commit
254779a324
@ -8,8 +8,8 @@ android {
|
||||
applicationId "org.mian.gitnex"
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 34
|
||||
versionCode 540
|
||||
versionName "5.4.0"
|
||||
versionCode 545
|
||||
versionName "5.5.0-dev"
|
||||
multiDexEnabled true
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
compileSdk 34
|
||||
@ -71,9 +71,9 @@ dependencies {
|
||||
implementation 'com.google.code.gson:gson:2.10.1'
|
||||
implementation "com.squareup.picasso:picasso:2.71828"
|
||||
implementation 'com.github.ramseth001:TextDrawable:1.1.3'
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
|
||||
implementation 'com.squareup.retrofit2:converter-scalars:2.9.0'
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.11.0'
|
||||
implementation 'com.squareup.retrofit2:converter-gson:2.11.0'
|
||||
implementation 'com.squareup.retrofit2:converter-scalars:2.11.0'
|
||||
implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.12'
|
||||
implementation 'org.ocpsoft.prettytime:prettytime:5.0.7.Final'
|
||||
implementation "com.github.skydoves:colorpickerview:2.3.0"
|
||||
|
@ -54,6 +54,7 @@ import org.mian.gitnex.fragments.RepositoriesFragment;
|
||||
import org.mian.gitnex.fragments.SettingsFragment;
|
||||
import org.mian.gitnex.fragments.StarredRepositoriesFragment;
|
||||
import org.mian.gitnex.fragments.WatchedRepositoriesFragment;
|
||||
import org.mian.gitnex.fragments.profile.DetailFragment;
|
||||
import org.mian.gitnex.helpers.AlertDialogs;
|
||||
import org.mian.gitnex.helpers.AppDatabaseSettings;
|
||||
import org.mian.gitnex.helpers.AppUtil;
|
||||
@ -298,10 +299,6 @@ public class MainActivity extends BaseActivity
|
||||
.setVisible(false);
|
||||
}
|
||||
|
||||
if (getAccount().requiresVersion("1.14.0")) {
|
||||
navigationView.getMenu().findItem(R.id.nav_my_issues).setVisible(true);
|
||||
}
|
||||
|
||||
if (getAccount().requiresVersion("1.20.0")) {
|
||||
navigationView.getMenu().findItem(R.id.nav_dashboard).setVisible(true);
|
||||
}
|
||||
@ -586,6 +583,10 @@ public class MainActivity extends BaseActivity
|
||||
this.overridePendingTransition(0, 0);
|
||||
refActivity = false;
|
||||
}
|
||||
if (DetailFragment.refProfile) {
|
||||
loadUserInfo();
|
||||
DetailFragment.refProfile = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void setActionBarTitle(String title) {
|
||||
|
@ -71,7 +71,7 @@ public class AttachmentsAdapter extends RecyclerView.Adapter<AttachmentsAdapter.
|
||||
}
|
||||
}
|
||||
|
||||
class ViewHolder extends RecyclerView.ViewHolder {
|
||||
public class ViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public TextView filename;
|
||||
public ImageView delete;
|
||||
|
@ -34,7 +34,6 @@ import org.mian.gitnex.helpers.ColorInverter;
|
||||
import org.mian.gitnex.helpers.LabelWidthCalculator;
|
||||
import org.mian.gitnex.helpers.RoundedTransformation;
|
||||
import org.mian.gitnex.helpers.TimeHelper;
|
||||
import org.mian.gitnex.helpers.TinyDB;
|
||||
import org.mian.gitnex.helpers.contexts.IssueContext;
|
||||
import org.mian.gitnex.helpers.contexts.RepositoryContext;
|
||||
|
||||
@ -44,7 +43,6 @@ import org.mian.gitnex.helpers.contexts.RepositoryContext;
|
||||
public class ExploreIssuesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||
|
||||
private final Context context;
|
||||
private final TinyDB tinyDb;
|
||||
private List<Issue> searchedList;
|
||||
private OnLoadMoreListener loadMoreListener;
|
||||
private boolean isLoading = false, isMoreDataAvailable = true;
|
||||
@ -52,7 +50,6 @@ public class ExploreIssuesAdapter extends RecyclerView.Adapter<RecyclerView.View
|
||||
public ExploreIssuesAdapter(List<Issue> dataList, Context ctx) {
|
||||
this.context = ctx;
|
||||
this.searchedList = dataList;
|
||||
this.tinyDb = TinyDB.getInstance(context);
|
||||
}
|
||||
|
||||
@NonNull @Override
|
||||
|
@ -1,18 +1,28 @@
|
||||
package org.mian.gitnex.fragments.profile;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Locale;
|
||||
import okhttp3.ResponseBody;
|
||||
import org.gitnex.tea4j.v2.models.Repository;
|
||||
import org.gitnex.tea4j.v2.models.UpdateUserAvatarOption;
|
||||
import org.gitnex.tea4j.v2.models.User;
|
||||
import org.gitnex.tea4j.v2.models.UserSettings;
|
||||
import org.gitnex.tea4j.v2.models.UserSettingsOptions;
|
||||
@ -21,6 +31,7 @@ import org.mian.gitnex.activities.BaseActivity;
|
||||
import org.mian.gitnex.activities.ProfileActivity;
|
||||
import org.mian.gitnex.clients.PicassoService;
|
||||
import org.mian.gitnex.clients.RetrofitClient;
|
||||
import org.mian.gitnex.databinding.CustomEditAvatarDialogBinding;
|
||||
import org.mian.gitnex.databinding.CustomEditProfileBinding;
|
||||
import org.mian.gitnex.databinding.FragmentProfileDetailBinding;
|
||||
import org.mian.gitnex.helpers.AlertDialogs;
|
||||
@ -29,7 +40,6 @@ import org.mian.gitnex.helpers.ClickListener;
|
||||
import org.mian.gitnex.helpers.Markdown;
|
||||
import org.mian.gitnex.helpers.RoundedTransformation;
|
||||
import org.mian.gitnex.helpers.TimeHelper;
|
||||
import org.mian.gitnex.helpers.TinyDB;
|
||||
import org.mian.gitnex.helpers.Toasty;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
@ -41,13 +51,17 @@ public class DetailFragment extends Fragment {
|
||||
|
||||
private static final String usernameBundle = "";
|
||||
Locale locale;
|
||||
TinyDB tinyDb;
|
||||
private Context context;
|
||||
private FragmentProfileDetailBinding binding;
|
||||
private String username;
|
||||
private CustomEditProfileBinding customEditProfileBinding;
|
||||
private CustomEditAvatarDialogBinding customEditAvatarDialogBinding;
|
||||
private MaterialAlertDialogBuilder materialAlertDialogBuilder;
|
||||
private AlertDialog dialogEditSettings;
|
||||
private AlertDialog dialogEditAvatar;
|
||||
private int imgRadius;
|
||||
private static Uri avatarUri = null;
|
||||
public static boolean refProfile = false;
|
||||
|
||||
public DetailFragment() {}
|
||||
|
||||
@ -73,8 +87,8 @@ public class DetailFragment extends Fragment {
|
||||
|
||||
binding = FragmentProfileDetailBinding.inflate(inflater, container, false);
|
||||
context = getContext();
|
||||
tinyDb = TinyDB.getInstance(context);
|
||||
locale = getResources().getConfiguration().locale;
|
||||
imgRadius = AppUtil.getPixelsFromDensity(context, 3);
|
||||
|
||||
getProfileDetail(username);
|
||||
getProfileRepository(username);
|
||||
@ -94,21 +108,121 @@ public class DetailFragment extends Fragment {
|
||||
((ProfileActivity) requireActivity()).viewPager.setCurrentItem(2));
|
||||
|
||||
if (username.equals(((BaseActivity) context).getAccount().getAccount().getUserName())) {
|
||||
binding.editProfile.setVisibility(View.VISIBLE);
|
||||
binding.metaProfile.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
binding.editProfile.setVisibility(View.GONE);
|
||||
binding.metaProfile.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
binding.editProfile.setOnClickListener(
|
||||
binding.updateProfile.setOnClickListener(
|
||||
editProfileSettings -> {
|
||||
customEditProfileBinding =
|
||||
CustomEditProfileBinding.inflate(LayoutInflater.from(context));
|
||||
showEditProfileDialog();
|
||||
});
|
||||
|
||||
binding.updateAvatar.setOnClickListener(updateAvatar -> openFileAttachment());
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
avatarUri = null;
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
ActivityResultLauncher<Intent> activityForAvatarUpdate =
|
||||
registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> {
|
||||
if (result.getResultCode() == Activity.RESULT_OK) {
|
||||
Intent data = result.getData();
|
||||
assert data != null;
|
||||
avatarUri = data.getData();
|
||||
if (avatarUri != null) {
|
||||
customEditAvatarDialogBinding =
|
||||
CustomEditAvatarDialogBinding.inflate(
|
||||
LayoutInflater.from(context));
|
||||
showUpdateAvatarDialog();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
private void openFileAttachment() {
|
||||
|
||||
String[] mimeTypes = {"image/webp", "image/gif", "image/jpg", "image/jpeg", "image/png"};
|
||||
Intent data = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
data.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
data.setType("image/*");
|
||||
data.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
|
||||
Intent intent = Intent.createChooser(data, "Choose an image");
|
||||
activityForAvatarUpdate.launch(intent);
|
||||
}
|
||||
|
||||
private void showUpdateAvatarDialog() {
|
||||
|
||||
View view = customEditAvatarDialogBinding.getRoot();
|
||||
materialAlertDialogBuilder.setView(view);
|
||||
|
||||
PicassoService.getInstance(context)
|
||||
.get()
|
||||
.load(avatarUri)
|
||||
.transform(new RoundedTransformation(imgRadius, 0))
|
||||
.placeholder(R.drawable.loader_animated)
|
||||
.resize(180, 180)
|
||||
.centerCrop()
|
||||
.into(customEditAvatarDialogBinding.userAvatar);
|
||||
|
||||
customEditAvatarDialogBinding.save.setOnClickListener(
|
||||
saveUserAvatar -> saveUserAvatar(avatarUri));
|
||||
|
||||
dialogEditAvatar = materialAlertDialogBuilder.show();
|
||||
}
|
||||
|
||||
private void saveUserAvatar(Uri avatar) {
|
||||
|
||||
InputStream imageStream = null;
|
||||
try {
|
||||
imageStream = context.getContentResolver().openInputStream(avatar);
|
||||
} catch (FileNotFoundException e) {
|
||||
e.getMessage();
|
||||
}
|
||||
Bitmap selectedImage = BitmapFactory.decodeStream(imageStream);
|
||||
|
||||
String encodedString = AppUtil.imageEncodeToBase64(selectedImage);
|
||||
|
||||
UpdateUserAvatarOption updateUserAvatarOption = new UpdateUserAvatarOption();
|
||||
updateUserAvatarOption.setImage(encodedString);
|
||||
|
||||
Call<Void> saveUserAvatar =
|
||||
RetrofitClient.getApiInterface(context).userUpdateAvatar(updateUserAvatarOption);
|
||||
|
||||
saveUserAvatar.enqueue(
|
||||
new Callback<>() {
|
||||
|
||||
@Override
|
||||
public void onResponse(
|
||||
@NonNull Call<Void> call, @NonNull retrofit2.Response<Void> response) {
|
||||
|
||||
if (response.code() == 204) {
|
||||
|
||||
dialogEditAvatar.dismiss();
|
||||
getProfileDetail(username);
|
||||
Toasty.success(context, getString(R.string.profileUpdate));
|
||||
refProfile = true;
|
||||
} else {
|
||||
|
||||
Toasty.error(context, getString(R.string.genericError));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<Void> call, @NonNull Throwable t) {
|
||||
|
||||
Toasty.error(context, getString(R.string.genericServerResponseError));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showEditProfileDialog() {
|
||||
|
||||
View view = customEditProfileBinding.getRoot();
|
||||
@ -160,7 +274,8 @@ public class DetailFragment extends Fragment {
|
||||
|
||||
dialogEditSettings.dismiss();
|
||||
getProfileDetail(username);
|
||||
Toasty.success(context, getString(R.string.settingsSave));
|
||||
Toasty.success(context, getString(R.string.profileUpdate));
|
||||
refProfile = true;
|
||||
} else {
|
||||
|
||||
Toasty.error(context, getString(R.string.genericError));
|
||||
@ -246,8 +361,6 @@ public class DetailFragment extends Fragment {
|
||||
? response.body().getEmail()
|
||||
: "";
|
||||
|
||||
int imgRadius = AppUtil.getPixelsFromDensity(context, 3);
|
||||
|
||||
binding.userFullName.setText(username);
|
||||
binding.userLogin.setText(
|
||||
getString(
|
||||
|
@ -12,6 +12,7 @@ import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Typeface;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
@ -24,6 +25,7 @@ import androidx.annotation.ColorInt;
|
||||
import androidx.browser.customtabs.CustomTabColorSchemeParams;
|
||||
import androidx.browser.customtabs.CustomTabsIntent;
|
||||
import androidx.core.content.pm.PackageInfoCompat;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
@ -413,6 +415,15 @@ public class AppUtil {
|
||||
return base64Str;
|
||||
}
|
||||
|
||||
public static String imageEncodeToBase64(Bitmap image) {
|
||||
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
image.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
|
||||
byte[] bytes = byteArrayOutputStream.toByteArray();
|
||||
|
||||
return Base64.encodeToString(bytes, Base64.DEFAULT);
|
||||
}
|
||||
|
||||
public static String decodeBase64(String str) {
|
||||
|
||||
String base64Str = str;
|
||||
|
59
app/src/main/res/layout/custom_edit_avatar_dialog.xml
Normal file
59
app/src/main/res/layout/custom_edit_avatar_dialog.xml
Normal file
@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="@dimen/dimen8dp">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/mainView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/dimen16dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="@dimen/dimen140dp"
|
||||
android:layout_height="@dimen/dimen140dp"
|
||||
style="?attr/materialCardViewFilledStyle"
|
||||
android:layout_marginBottom="@dimen/dimen8dp"
|
||||
app:cardElevation="@dimen/dimen0dp"
|
||||
app:cardCornerRadius="@dimen/dimen48dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/userAvatar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:contentDescription="@string/generalImgContentText"
|
||||
android:src="@mipmap/app_logo_round" />
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/save"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dimen54dp"
|
||||
android:layout_marginTop="@dimen/dimen16dp"
|
||||
android:text="@string/saveButton"
|
||||
android:textColor="?attr/materialCardBackgroundColor"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
</LinearLayout>
|
@ -91,17 +91,35 @@
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="@dimen/dimen14sp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/editProfile"
|
||||
<com.google.android.material.button.MaterialButtonToggleGroup
|
||||
android:id="@+id/metaProfile"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/editSettings"
|
||||
android:textColor="?attr/materialCardBackgroundColor"
|
||||
android:backgroundTint="?attr/fabColor"
|
||||
app:iconTint="?attr/materialCardBackgroundColor"
|
||||
app:icon="@drawable/ic_edit"
|
||||
android:textSize="14sp"
|
||||
android:visibility="gone" />
|
||||
android:visibility="gone">
|
||||
|
||||
<Button
|
||||
android:id="@+id/update_avatar"
|
||||
style="?attr/materialButtonToggleGroupStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:iconTint="?attr/materialCardBackgroundColor"
|
||||
app:icon="@drawable/ic_person"
|
||||
android:textColor="?attr/materialCardBackgroundColor"
|
||||
android:textSize="@dimen/dimen14sp"
|
||||
android:text="@string/userAvatar" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/update_profile"
|
||||
style="?attr/materialButtonToggleGroupStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:iconTint="?attr/materialCardBackgroundColor"
|
||||
app:icon="@drawable/ic_edit"
|
||||
android:textColor="?attr/materialCardBackgroundColor"
|
||||
android:textSize="@dimen/dimen14sp"
|
||||
android:text="@string/navProfile" />
|
||||
|
||||
</com.google.android.material.button.MaterialButtonToggleGroup>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
@ -360,6 +360,7 @@
|
||||
<string name="editSettings">Edit Profile</string>
|
||||
<string name="hideActivity">Hide Activity from profile page</string>
|
||||
<string name="hideEmail">Hide Email</string>
|
||||
<string name="profileUpdate">Profile updated</string>
|
||||
<!-- profile section -->
|
||||
|
||||
<!-- account settings -->
|
||||
|
@ -1,25 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<changelog>
|
||||
|
||||
<release version="5.4.0" versioncode="540">
|
||||
<change>New: Restore/import app data</change>
|
||||
<change>New: Backup/export app data</change>
|
||||
<change>New: Delete email from account</change>
|
||||
<change>New: Update profile settings</change>
|
||||
<change>New: Add SSH key to account</change>
|
||||
<change>Improvement: Open specific URL in deep links</change>
|
||||
<change>Improvement: Show open in browser with other buttons in deep links</change>
|
||||
<change>Improvement: Show pinned messages in timeline</change>
|
||||
<change>Improvement: Hide Comment button if issue is locked</change>
|
||||
<change>Improvement: New languages and translation updates</change>
|
||||
<change>Bugfix: Fix crash on dynamic snackbar colors for older Android versions</change>
|
||||
<change>Bugfix: Fix missing text after links in issue/pr</change>
|
||||
<change>Bugfix: Fix emoji parsing in code blocks in issue/pr</change>
|
||||
<change>Bugfix: Fix crash on null objects in timeline from API</change>
|
||||
<change>Bugfix: Fix not seeing labels for repo admin when code option is disabled</change>
|
||||
<change>Bugfix: Fix Task list in Markdown</change>
|
||||
<change>Bugfix: Fix other issues related to Markdown</change>
|
||||
<change>Bugfix: Fix crash on images with no path</change>
|
||||
<release version="5.5.0-dev" versioncode="545">
|
||||
<change>Under development</change>
|
||||
</release>
|
||||
|
||||
</changelog>
|
||||
|
Loading…
Reference in New Issue
Block a user