Improve profile upload animation

// FREEBIE
This commit is contained in:
Moxie Marlinspike 2017-10-01 18:11:42 -07:00
parent 893cf9c01d
commit 4c2269175b
5 changed files with 186 additions and 104 deletions

View File

@ -387,6 +387,7 @@
<activity android:name=".CreateProfileActivity" <activity android:name=".CreateProfileActivity"
android:theme="@style/TextSecure.LightTheme" android:theme="@style/TextSecure.LightTheme"
android:windowSoftInputMode="stateVisible"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/> android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".ClearProfileAvatarActivity" <activity android:name=".ClearProfileAvatarActivity"

View File

@ -106,6 +106,7 @@ dependencies {
exclude group: 'com.android.support', module: 'recyclerview-v7' exclude group: 'com.android.support', module: 'recyclerview-v7'
} }
compile 'com.codewaves.stickyheadergrid:stickyheadergrid:0.9.4' compile 'com.codewaves.stickyheadergrid:stickyheadergrid:0.9.4'
compile 'com.github.dmytrodanylyk.circular-progress-button:library:1.1.3'
testCompile 'junit:junit:4.12' testCompile 'junit:junit:4.12'
@ -173,6 +174,7 @@ dependencyVerification {
'com.annimon:stream:5da6e2e3e0551d61a3ea7014f04312276549e3dd739cf637996e4cf43c5535b9', 'com.annimon:stream:5da6e2e3e0551d61a3ea7014f04312276549e3dd739cf637996e4cf43c5535b9',
'com.takisoft.fix:colorpicker:f5d0dbabe406a1800498ca9c1faf34db36e021d8488bf10360f29961fe3ab0d1', 'com.takisoft.fix:colorpicker:f5d0dbabe406a1800498ca9c1faf34db36e021d8488bf10360f29961fe3ab0d1',
'com.codewaves.stickyheadergrid:stickyheadergrid:5b4aa6a52a957cfd55f60f4220c11c0c371385a3cb9786cae03c260dcdef5794', 'com.codewaves.stickyheadergrid:stickyheadergrid:5b4aa6a52a957cfd55f60f4220c11c0c371385a3cb9786cae03c260dcdef5794',
'com.github.dmytrodanylyk.circular-progress-button:library:635882453475181d737719b2adef658d80f9f85c9bdaf022f06cd22f387cdb16',
'com.android.support:support-annotations:a774272036941b4e912eb426d70c848bde7f06a3bf5fb491f75a427dc6595270', 'com.android.support:support-annotations:a774272036941b4e912eb426d70c848bde7f06a3bf5fb491f75a427dc6595270',
'com.android.support:support-v4:ee44c481a1f4d6978568e223e8125379b52b2ececdd53450e09ebae144bd377d', 'com.android.support:support-v4:ee44c481a1f4d6978568e223e8125379b52b2ececdd53450e09ebae144bd377d',
'com.android.support:support-vector-drawable:077009d13882ee96f061e4bc2dbe7cce7ae1762d8297592a787ff741afbfb1f2', 'com.android.support:support-vector-drawable:077009d13882ee96f061e4bc2dbe7cce7ae1762d8297592a787ff741afbfb1f2',

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:color="@color/textsecure_primary"/>
<item android:state_focused="true"
android:color="@color/textsecure_primary"/>
<item android:state_enabled="false"
android:color="@color/textsecure_primary"/>
<item android:state_enabled="true"
android:color="@color/textsecure_primary"/>
</selector>

View File

@ -1,7 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<org.thoughtcrime.securesms.components.InputAwareLayout <FrameLayout android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<org.thoughtcrime.securesms.components.InputAwareLayout
android:id="@+id/container" android:id="@+id/container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
@ -77,15 +81,21 @@
android:clipChildren="false" android:clipChildren="false"
android:clipToPadding="false"> android:clipToPadding="false">
<Button android:id="@+id/finish_button" <com.dd.CircularProgressButton
android:text="@string/profile_create_activity__finish" android:id="@+id/finish_button"
app:cpb_textIdle="@string/profile_create_activity__finish"
app:cpb_selectorIdle="@drawable/progress_button_state"
app:cpb_colorIndicator="@color/white"
app:cpb_colorProgress="@color/textsecure_primary"
app:cpb_cornerRadius="50dp"
android:textAllCaps="true" android:textAllCaps="true"
android:background="@color/signal_primary" android:background="@color/signal_primary"
android:textColor="@color/white" android:textColor="@color/white"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="50dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="20dp" android:layout_marginBottom="20dp"
android:padding="10dp"
android:layout_gravity="center_horizontal"/> android:layout_gravity="center_horizontal"/>
<TextView android:id="@+id/skip_button" <TextView android:id="@+id/skip_button"
@ -105,4 +115,13 @@
android:visibility="gone" /> android:visibility="gone" />
</LinearLayout> </LinearLayout>
</org.thoughtcrime.securesms.components.InputAwareLayout> </org.thoughtcrime.securesms.components.InputAwareLayout>
<View android:id="@+id/reveal"
android:background="@color/textsecure_primary"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible"/>
</FrameLayout>

View File

@ -1,27 +1,33 @@
package org.thoughtcrime.securesms; package org.thoughtcrime.securesms;
import android.animation.Animator;
import android.app.Activity; import android.app.Activity;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.provider.MediaStore; import android.provider.MediaStore;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.text.Editable; import android.text.Editable;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.util.Log; import android.util.Log;
import android.util.Pair; import android.util.Pair;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText; import android.widget.EditText;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.dd.CircularProgressButton;
import com.soundcloud.android.crop.Crop; import com.soundcloud.android.crop.Crop;
import org.thoughtcrime.securesms.components.InputAwareLayout; import org.thoughtcrime.securesms.components.InputAwareLayout;
@ -43,7 +49,6 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture; import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.crypto.ProfileCipher; import org.whispersystems.signalservice.api.crypto.ProfileCipher;
import org.whispersystems.signalservice.api.util.StreamDetails; import org.whispersystems.signalservice.api.util.StreamDetails;
@ -72,10 +77,11 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
private InputAwareLayout container; private InputAwareLayout container;
private ImageView avatar; private ImageView avatar;
private Button finishButton; private CircularProgressButton finishButton;
private EditText name; private EditText name;
private EmojiToggle emojiToggle; private EmojiToggle emojiToggle;
private EmojiDrawer emojiDrawer; private EmojiDrawer emojiDrawer;
private View reveal;
private Intent nextIntent; private Intent nextIntent;
private byte[] avatarBytes; private byte[] avatarBytes;
@ -161,6 +167,7 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
this.emojiDrawer = ViewUtil.findById(this, R.id.emoji_drawer); this.emojiDrawer = ViewUtil.findById(this, R.id.emoji_drawer);
this.container = ViewUtil.findById(this, R.id.container); this.container = ViewUtil.findById(this, R.id.container);
this.finishButton = ViewUtil.findById(this, R.id.finish_button); this.finishButton = ViewUtil.findById(this, R.id.finish_button);
this.reveal = ViewUtil.findById(this, R.id.reveal);
this.nextIntent = getIntent().getParcelableExtra(NEXT_INTENT); this.nextIntent = getIntent().getParcelableExtra(NEXT_INTENT);
this.avatar.setImageDrawable(ContactPhotoFactory.getResourceContactPhoto(R.drawable.ic_camera_alt_white_24dp) this.avatar.setImageDrawable(ContactPhotoFactory.getResourceContactPhoto(R.drawable.ic_camera_alt_white_24dp)
@ -196,6 +203,8 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
}); });
this.finishButton.setOnClickListener(view -> { this.finishButton.setOnClickListener(view -> {
this.finishButton.setIndeterminateProgressMode(true);
this.finishButton.setProgress(50);
handleUpload(); handleUpload();
}); });
@ -350,17 +359,15 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
else avatar = new StreamDetails(new ByteArrayInputStream(avatarBytes), else avatar = new StreamDetails(new ByteArrayInputStream(avatarBytes),
"image/jpeg", avatarBytes.length); "image/jpeg", avatarBytes.length);
new ProgressDialogAsyncTask<Void, Void, Boolean>(this, new AsyncTask<Void, Void, Boolean>() {
getString(R.string.CreateProfileActivity_updating_and_encrypting_profile),
getString(R.string.CreateProfileActivity_updating_profile))
{
@Override @Override
protected Boolean doInBackground(Void... params) { protected Boolean doInBackground(Void... params) {
Context context = CreateProfileActivity.this;
byte[] profileKey = ProfileKeyUtil.getProfileKey(CreateProfileActivity.this); byte[] profileKey = ProfileKeyUtil.getProfileKey(CreateProfileActivity.this);
try { try {
accountManager.setProfileName(profileKey, name); accountManager.setProfileName(profileKey, name);
TextSecurePreferences.setProfileName(getContext(), name); TextSecurePreferences.setProfileName(context, name);
} catch (IOException e) { } catch (IOException e) {
Log.w(TAG, e); Log.w(TAG, e);
return false; return false;
@ -368,13 +375,13 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
try { try {
accountManager.setProfileAvatar(profileKey, avatar); accountManager.setProfileAvatar(profileKey, avatar);
AvatarHelper.setAvatar(getContext(), Address.fromSerialized(TextSecurePreferences.getLocalNumber(getContext())), avatarBytes); AvatarHelper.setAvatar(CreateProfileActivity.this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)), avatarBytes);
} catch (IOException e) { } catch (IOException e) {
Log.w(TAG, e); Log.w(TAG, e);
return false; return false;
} }
ApplicationContext.getInstance(getContext()).getJobManager().add(new MultiDeviceProfileKeyUpdateJob(getContext())); ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceProfileKeyUpdateJob(context));
return true; return true;
} }
@ -385,9 +392,8 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
if (result) { if (result) {
if (captureFile != null) captureFile.delete(); if (captureFile != null) captureFile.delete();
if (nextIntent != null) startActivity(nextIntent); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) handleFinishedLollipop();
else handleFinishedLegacy();
finish();
} else { } else {
Toast.makeText(CreateProfileActivity.this, R.string.CreateProfileActivity_problem_setting_profile, Toast.LENGTH_LONG).show(); Toast.makeText(CreateProfileActivity.this, R.string.CreateProfileActivity_problem_setting_profile, Toast.LENGTH_LONG).show();
} }
@ -395,5 +401,48 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
}.execute(); }.execute();
} }
private void handleFinishedLegacy() {
finishButton.setProgress(0);
if (nextIntent != null) startActivity(nextIntent);
finish();
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void handleFinishedLollipop() {
int[] finishButtonLocation = new int[2];
int[] revealLocation = new int[2];
finishButton.getLocationInWindow(finishButtonLocation);
reveal.getLocationInWindow(revealLocation);
int finishX = finishButtonLocation[0] - revealLocation[0];
int finishY = finishButtonLocation[1] - revealLocation[1];
finishX += finishButton.getWidth() / 2;
finishY += finishButton.getHeight() / 2;
Animator animation = ViewAnimationUtils.createCircularReveal(reveal, finishX, finishY, 0f, (float) Math.max(reveal.getWidth(), reveal.getHeight()));
animation.setDuration(500);
animation.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {}
@Override
public void onAnimationEnd(Animator animation) {
finishButton.setProgress(0);
if (nextIntent != null) startActivity(nextIntent);
finish();
}
@Override
public void onAnimationCancel(Animator animation) {}
@Override
public void onAnimationRepeat(Animator animation) {}
});
reveal.setVisibility(View.VISIBLE);
animation.start();
}
} }