mirror of
				https://framagit.org/tom79/fedilab-tube
				synced 2025-06-05 21:09:11 +02:00 
			
		
		
		
	Allow to edit profile + profile picture
This commit is contained in:
		| @@ -105,6 +105,7 @@ dependencies { | ||||
|     implementation ("androidx.navigation:navigation-dynamic-features-fragment:2.3.1") | ||||
|     implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' | ||||
|     implementation 'androidx.browser:browser:1.2.0' | ||||
|     implementation 'androidx.documentfile:documentfile:1.0.1' | ||||
|     testImplementation 'junit:junit:4.13' | ||||
|     androidTestImplementation 'androidx.test.ext:junit:1.1.2' | ||||
|     androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' | ||||
|   | ||||
| @@ -13,6 +13,8 @@ | ||||
|     <string name="set_autoplay_next_video_choice" translatable="false">set_autoplay_next_video_choice</string> | ||||
|     <string name="set_store_in_history">set_store_in_history</string> | ||||
|  | ||||
|     <string name="change_profile_picture">Modifier la photo de profil</string> | ||||
|     <string name="account_updated">Le compte a été mis à jour !</string> | ||||
|  | ||||
|     <string name="save">Enregistrer</string> | ||||
|     <string name="set_autoplay_next_video">Lire automatiquement la vidéo suivante</string> | ||||
|   | ||||
| @@ -14,7 +14,7 @@ | ||||
|  | ||||
|     <string name="save">Save</string> | ||||
|     <string name="enable_history">Enable history</string> | ||||
|  | ||||
|     <string name="change_profile_picture">Change profile picture</string> | ||||
|     <string name="set_autoplay">Automatic playback</string> | ||||
|     <string name="set_autoplay_description">If enabled, videos will be played automatically</string> | ||||
|  | ||||
| @@ -87,6 +87,7 @@ | ||||
|  | ||||
|     <string name="download_file">Download %1$s</string> | ||||
|  | ||||
|     <string name="account_updated">The account has been updated!</string> | ||||
|  | ||||
|     <string name="action_privacy">Privacy</string> | ||||
|     <string name="action_logout">Logout</string> | ||||
|   | ||||
| @@ -18,7 +18,7 @@ | ||||
|     <application | ||||
|         android:name=".FedilabTube" | ||||
|         android:allowBackup="false" | ||||
|  | ||||
|         android:requestLegacyExternalStorage="true" | ||||
|         android:icon="@mipmap/ic_launcher" | ||||
|         android:label="@string/app_name" | ||||
|         android:roundIcon="@mipmap/ic_launcher_round" | ||||
|   | ||||
| @@ -1,17 +1,37 @@ | ||||
| package app.fedilab.fedilabtube; | ||||
|  | ||||
| import android.Manifest; | ||||
| import android.app.Activity; | ||||
| import android.content.Intent; | ||||
| import android.content.pm.PackageManager; | ||||
| import android.net.Uri; | ||||
| import android.os.Bundle; | ||||
| import android.os.Handler; | ||||
| import android.os.Looper; | ||||
| import android.view.MenuItem; | ||||
| import android.view.View; | ||||
| import android.widget.Toast; | ||||
|  | ||||
| import androidx.appcompat.app.AppCompatActivity; | ||||
| import androidx.core.app.ActivityCompat; | ||||
| import androidx.core.content.ContextCompat; | ||||
| import androidx.documentfile.provider.DocumentFile; | ||||
|  | ||||
| import com.bumptech.glide.Glide; | ||||
| import com.bumptech.glide.load.resource.bitmap.CenterCrop; | ||||
| import com.bumptech.glide.load.resource.bitmap.RoundedCorners; | ||||
| import com.bumptech.glide.request.RequestOptions; | ||||
|  | ||||
| import app.fedilab.fedilabtube.client.RetrofitPeertubeAPI; | ||||
| import app.fedilab.fedilabtube.client.entities.Error; | ||||
| import app.fedilab.fedilabtube.client.entities.UserMe; | ||||
| import app.fedilab.fedilabtube.client.entities.UserSettings; | ||||
| import app.fedilab.fedilabtube.databinding.ActivityMyAccountSettingsBinding; | ||||
| import app.fedilab.fedilabtube.helper.Helper; | ||||
| import es.dmoral.toasty.Toasty; | ||||
|  | ||||
| import static app.fedilab.fedilabtube.PeertubeUploadActivity.MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE; | ||||
|  | ||||
| /* Copyright 2020 Thomas Schneider | ||||
|  * | ||||
|  * This file is a part of TubeLab | ||||
| @@ -30,6 +50,9 @@ import es.dmoral.toasty.Toasty; | ||||
| public class MyAccountActivity extends AppCompatActivity { | ||||
|  | ||||
|     ActivityMyAccountSettingsBinding binding; | ||||
|     private static final int PICK_IMAGE = 466; | ||||
|     private Uri inputData; | ||||
|     private  String fileName; | ||||
|  | ||||
|     @Override | ||||
|     protected void onCreate(Bundle savedInstanceState) { | ||||
| @@ -48,19 +71,65 @@ public class MyAccountActivity extends AppCompatActivity { | ||||
|         binding.displayname.setText(MainActivity.userMe.getAccount().getDisplayName()); | ||||
|         binding.description.setText(MainActivity.userMe.getAccount().getDescription()); | ||||
|  | ||||
|         binding.save.setOnClickListener(v -> new Thread(() -> { | ||||
|             UserSettings userSettings = new UserSettings(); | ||||
|             userSettings.setDisplayName(binding.displayname.getText().toString().trim()); | ||||
|             userSettings.setDescription(binding.description.getText().toString().trim()); | ||||
|             try { | ||||
|                 RetrofitPeertubeAPI api = new RetrofitPeertubeAPI(MyAccountActivity.this); | ||||
|                 api.updateUser(userSettings); | ||||
|                 MainActivity.userMe.getAccount().setDisplayName(binding.displayname.getText().toString().trim()); | ||||
|                 MainActivity.userMe.getAccount().setDescription(binding.description.getText().toString().trim()); | ||||
|             } catch (Exception | Error e) { | ||||
|                 Toasty.error(MyAccountActivity.this, getString(R.string.toast_error), Toasty.LENGTH_LONG).show(); | ||||
|         Helper.loadGiF(MyAccountActivity.this, MainActivity.userMe.getAccount().getAvatar()!=null?MainActivity.userMe.getAccount().getAvatar().getPath():null, binding.profilePicture); | ||||
|  | ||||
|         binding.selectFile.setOnClickListener(v->{ | ||||
|             if (ContextCompat.checkSelfPermission(MyAccountActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != | ||||
|                     PackageManager.PERMISSION_GRANTED) { | ||||
|                 ActivityCompat.requestPermissions(MyAccountActivity.this, | ||||
|                         new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, | ||||
|                         MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE); | ||||
|                 return; | ||||
|             } | ||||
|         }).start()); | ||||
|  | ||||
|             Intent intent = new Intent(Intent.ACTION_GET_CONTENT); | ||||
|             intent.addCategory(Intent.CATEGORY_OPENABLE); | ||||
|             intent.setType("*/*"); | ||||
|             String[] mimetypes = {"image/*"}; | ||||
|             intent.putExtra(Intent.EXTRA_MIME_TYPES, mimetypes); | ||||
|             startActivityForResult(intent, PICK_IMAGE); | ||||
|         }); | ||||
|  | ||||
|  | ||||
|         binding.save.setOnClickListener(v -> { | ||||
|             binding.save.setEnabled(false); | ||||
|             new Thread(() -> { | ||||
|                 UserSettings userSettings = new UserSettings(); | ||||
|                 if( binding.displayname.getText() != null) { | ||||
|                     userSettings.setDisplayName(binding.displayname.getText().toString().trim()); | ||||
|                 } | ||||
|                 if( binding.description.getText() != null) { | ||||
|                     userSettings.setDescription(binding.description.getText().toString().trim()); | ||||
|                 } | ||||
|                 if( inputData != null ) { | ||||
|                     userSettings.setAvatarfile(inputData); | ||||
|                     userSettings.setFileName(fileName); | ||||
|                 } | ||||
|                 try { | ||||
|                     RetrofitPeertubeAPI api = new RetrofitPeertubeAPI(MyAccountActivity.this); | ||||
|                     UserMe.AvatarResponse avatarResponse = api.updateUser(userSettings); | ||||
|                     MainActivity.userMe.getAccount().setDisplayName(binding.displayname.getText().toString().trim()); | ||||
|                     MainActivity.userMe.getAccount().setDescription(binding.description.getText().toString().trim()); | ||||
|                     if( avatarResponse != null && avatarResponse.getAvatar() != null ) { | ||||
|                         MainActivity.userMe.getAccount().setAvatar(avatarResponse.getAvatar()); | ||||
|                     } | ||||
|                     Handler mainHandler = new Handler(Looper.getMainLooper()); | ||||
|                     Runnable myRunnable = () -> { | ||||
|                         Toasty.info(MyAccountActivity.this, getString(R.string.account_updated), Toasty.LENGTH_LONG).show(); | ||||
|                         binding.save.setEnabled(true); | ||||
|                     }; | ||||
|                     mainHandler.post(myRunnable); | ||||
|                 } catch (Exception | Error e) { | ||||
|                     Handler mainHandler = new Handler(Looper.getMainLooper()); | ||||
|                     Runnable myRunnable = () -> { | ||||
|                         Toasty.error(MyAccountActivity.this, getString(R.string.toast_error), Toasty.LENGTH_LONG).show(); | ||||
|                         binding.save.setEnabled(true); | ||||
|                     }; | ||||
|                     mainHandler.post(myRunnable); | ||||
|  | ||||
|                 } | ||||
|             }).start(); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -71,4 +140,27 @@ public class MyAccountActivity extends AppCompatActivity { | ||||
|         } | ||||
|         return super.onOptionsItemSelected(item); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     @Override | ||||
|     public void onActivityResult(int requestCode, int resultCode, Intent data) { | ||||
|         super.onActivityResult(requestCode, resultCode, data); | ||||
|         if (requestCode == PICK_IMAGE && resultCode == Activity.RESULT_OK) { | ||||
|             if (data == null || data.getData() == null) { | ||||
|                 Toasty.error(MyAccountActivity.this, getString(R.string.toot_select_image_error), Toast.LENGTH_LONG).show(); | ||||
|                 return; | ||||
|             } | ||||
|             inputData = data.getData(); | ||||
|             DocumentFile documentFile = DocumentFile.fromSingleUri(this, inputData); | ||||
|             if (documentFile != null) { | ||||
|                 fileName = documentFile.getName(); | ||||
|             } | ||||
|             Glide.with(MyAccountActivity.this) | ||||
|                     .load(inputData) | ||||
|                     .thumbnail(0.1f) | ||||
|                     .apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10))) | ||||
|                     .into(binding.profilePicture); | ||||
|  | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -67,7 +67,7 @@ public class PeertubeUploadActivity extends AppCompatActivity { | ||||
|  | ||||
|  | ||||
|     private final int PICK_IVDEO = 52378; | ||||
|     private final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 724; | ||||
|     public static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 724; | ||||
|     private Button set_upload_file, set_upload_submit; | ||||
|     private Spinner set_upload_privacy, set_upload_channel; | ||||
|     private TextView set_upload_file_name; | ||||
|   | ||||
| @@ -138,7 +138,7 @@ public interface PeertubeService { | ||||
|  | ||||
|     @Multipart | ||||
|     @POST("users/me/avatar/pick") | ||||
|     Call<String> updateProfilePicture( | ||||
|     Call<UserMe.AvatarResponse> updateProfilePicture( | ||||
|             @Header("Authorization") String credentials, | ||||
|             @Part MultipartBody.Part avatarfile); | ||||
|  | ||||
|   | ||||
| @@ -21,9 +21,12 @@ import android.content.SharedPreferences; | ||||
| import android.database.sqlite.SQLiteDatabase; | ||||
| import android.os.Handler; | ||||
| import android.os.Looper; | ||||
| import android.webkit.MimeTypeMap; | ||||
|  | ||||
| import java.io.ByteArrayOutputStream; | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.io.UnsupportedEncodingException; | ||||
| import java.net.URL; | ||||
| import java.net.URLDecoder; | ||||
| @@ -451,8 +454,9 @@ public class RetrofitPeertubeAPI { | ||||
|      * | ||||
|      * @param userSettings UserSettings | ||||
|      */ | ||||
|     public void updateUser(UserSettings userSettings) throws IOException, Error { | ||||
|     public UserMe.AvatarResponse updateUser(UserSettings userSettings) throws IOException, Error { | ||||
|         APIResponse apiResponse = new APIResponse(); | ||||
|         UserMe.AvatarResponse avatarResponse = null; | ||||
|         PeertubeService peertubeService = init(); | ||||
|         Call<String> updateUser = peertubeService.updateUser(getToken(), | ||||
|                 userSettings.isVideosHistoryEnabled(), | ||||
| @@ -473,15 +477,45 @@ public class RetrofitPeertubeAPI { | ||||
|             if (response.errorBody() != null) { | ||||
|                 error.setError(response.errorBody().string()); | ||||
|             } else { | ||||
|  | ||||
|                 error.setError(_context.getString(R.string.toast_error)); | ||||
|             } | ||||
|             throw error; | ||||
|         } | ||||
|         if (userSettings.getAvatarfile() != null) { | ||||
|             RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), userSettings.getAvatarfile()); | ||||
|             MultipartBody.Part bodyThumbnail = MultipartBody.Part.createFormData("avatarfile", userSettings.getAvatarfile().getName(), requestFile); | ||||
|             Call<String> updateProfilePicture = peertubeService.updateProfilePicture(getToken(), bodyThumbnail); | ||||
|             InputStream inputStream = _context.getContentResolver().openInputStream(userSettings.getAvatarfile()); | ||||
|             ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream(); | ||||
|             int bufferSize = 1024; | ||||
|             byte[] buffer = new byte[bufferSize]; | ||||
|  | ||||
|             int len; | ||||
|             while ((len = inputStream.read(buffer)) != -1) { | ||||
|                 byteBuffer.write(buffer, 0, len); | ||||
|             } | ||||
|             byte[] imageBytes = byteBuffer.toByteArray(); | ||||
|             String mime = MimeTypeMap.getFileExtensionFromUrl(userSettings.getAvatarfile().toString()); | ||||
|             if( mime == null || mime.trim().length() == 0) { | ||||
|                 mime = "png"; | ||||
|             } | ||||
|             RequestBody requestFile = RequestBody.create(MediaType.parse("image/"+mime), imageBytes); | ||||
|             MultipartBody.Part bodyThumbnail = MultipartBody.Part.createFormData("avatarfile", userSettings.getFileName(), requestFile); | ||||
|             Call<UserMe.AvatarResponse> updateProfilePicture = peertubeService.updateProfilePicture(getToken(), bodyThumbnail); | ||||
|             Response<UserMe.AvatarResponse> responseAvatar = updateProfilePicture.execute(); | ||||
|             if (response.isSuccessful()) { | ||||
|                 avatarResponse = responseAvatar.body(); | ||||
|             } else { | ||||
|                 setError(apiResponse, response.code(), response.errorBody()); | ||||
|                 Error error = new Error(); | ||||
|                 error.setStatusCode(response.code()); | ||||
|                 if (response.errorBody() != null) { | ||||
|                     error.setError(response.errorBody().string()); | ||||
|                 } else { | ||||
|                     error.setError(_context.getString(R.string.toast_error)); | ||||
|                 } | ||||
|                 throw error; | ||||
|             } | ||||
|         } | ||||
|         return avatarResponse; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -265,4 +265,18 @@ public class UserMe { | ||||
|     public void setAutoPlayVideo(boolean autoPlayVideo) { | ||||
|         this.autoPlayVideo = autoPlayVideo; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public static class AvatarResponse { | ||||
|         @SerializedName("avatar") | ||||
|         private Avatar avatar; | ||||
|  | ||||
|         public Avatar getAvatar() { | ||||
|             return avatar; | ||||
|         } | ||||
|  | ||||
|         public void setAvatar(Avatar avatar) { | ||||
|             this.avatar = avatar; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -14,8 +14,9 @@ package app.fedilab.fedilabtube.client.entities; | ||||
|  * You should have received a copy of the GNU General Public License along with TubeLab; if not, | ||||
|  * see <http://www.gnu.org/licenses>. */ | ||||
|  | ||||
| import android.net.Uri; | ||||
|  | ||||
| import java.util.List; | ||||
| import java.io.File; | ||||
|  | ||||
| @SuppressWarnings({"unused", "RedundantSuppression"}) | ||||
| public class UserSettings { | ||||
| @@ -27,7 +28,8 @@ public class UserSettings { | ||||
|     private List<String> videoLanguages; | ||||
|     private String description; | ||||
|     private String displayName; | ||||
|     private File avatarfile; | ||||
|     private Uri avatarfile; | ||||
|     private String fileName; | ||||
|  | ||||
|     public Boolean isVideosHistoryEnabled() { | ||||
|         return videosHistoryEnabled; | ||||
| @@ -77,11 +79,11 @@ public class UserSettings { | ||||
|         this.displayName = displayName; | ||||
|     } | ||||
|  | ||||
|     public File getAvatarfile() { | ||||
|     public Uri getAvatarfile() { | ||||
|         return avatarfile; | ||||
|     } | ||||
|  | ||||
|     public void setAvatarfile(File avatarfile) { | ||||
|     public void setAvatarfile(Uri avatarfile) { | ||||
|         this.avatarfile = avatarfile; | ||||
|     } | ||||
|  | ||||
| @@ -104,4 +106,21 @@ public class UserSettings { | ||||
|     public void setAutoPlayNextVideo(Boolean autoPlayNextVideo) { | ||||
|         this.autoPlayNextVideo = autoPlayNextVideo; | ||||
|     } | ||||
|  | ||||
|     public Boolean getAutoPlayNextVideo() { | ||||
|         return autoPlayNextVideo; | ||||
|     } | ||||
|  | ||||
|     public String getFileName() { | ||||
|         return fileName; | ||||
|     } | ||||
|  | ||||
|     public void setFileName(String fileName) { | ||||
|         if( fileName == null) { | ||||
|             this.fileName = "avatar.png"; | ||||
|         } else { | ||||
|             this.fileName = fileName; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -5,17 +5,34 @@ | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="match_parent" | ||||
|     > | ||||
|  | ||||
|     <ImageView | ||||
|         android:layout_marginTop="50dp" | ||||
|         android:id="@+id/profile_picture" | ||||
|         android:layout_marginStart="50dp" | ||||
|         android:layout_width="80dp" | ||||
|         android:layout_height="80dp" | ||||
|         android:contentDescription="@string/profile_picture" | ||||
|         app:layout_constraintStart_toStartOf="parent" | ||||
|         app:layout_constraintTop_toTopOf="parent"/> | ||||
|     <Button | ||||
|         android:id="@+id/select_file" | ||||
|         style="@style/Widget.AppCompat.Button.Borderless" | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:singleLine="true" | ||||
|         android:text="@string/change_profile_picture" | ||||
|         app:layout_constraintStart_toEndOf="@+id/profile_picture" | ||||
|         app:layout_constraintBottom_toBottomOf="@id/profile_picture" /> | ||||
|     <com.google.android.material.textfield.TextInputLayout | ||||
|         android:id="@+id/displayname_container" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_marginTop="50dp" | ||||
|         android:layout_marginTop="30dp" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:layout_marginStart="50dp" | ||||
|         android:layout_marginEnd="50dp" | ||||
|         app:layout_constraintEnd_toEndOf="parent" | ||||
|         app:layout_constraintStart_toStartOf="parent" | ||||
|         app:layout_constraintTop_toTopOf="parent"> | ||||
|         app:layout_constraintTop_toBottomOf="@+id/profile_picture"> | ||||
|         <com.google.android.material.textfield.TextInputEditText | ||||
|             android:id="@+id/displayname" | ||||
|             android:layout_width="match_parent" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user