package com.tomclaw.mandarin.main; import android.content.ContentResolver; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Bitmap; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.support.v7.app.ActionBar; import android.support.v7.app.AlertDialog; import android.support.v7.widget.Toolbar; import android.text.TextUtils; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.OvershootInterpolator; import android.widget.FrameLayout; import android.widget.TextView; import android.widget.Toast; import android.widget.ViewSwitcher; import com.tomclaw.mandarin.R; import com.tomclaw.mandarin.core.BitmapCache; import com.tomclaw.mandarin.core.GlobalProvider; import com.tomclaw.mandarin.core.PleaseWaitTask; import com.tomclaw.mandarin.core.QueryHelper; import com.tomclaw.mandarin.core.RequestHelper; import com.tomclaw.mandarin.core.TaskExecutor; import com.tomclaw.mandarin.core.WeakObjectTask; import com.tomclaw.mandarin.im.icq.UserInfoRequest; import com.tomclaw.mandarin.main.views.ContactImage; import com.tomclaw.mandarin.util.BitmapHelper; import com.tomclaw.mandarin.util.HttpUtil; import com.tomclaw.mandarin.util.Logger; import java.lang.ref.WeakReference; /** * Created by Solkin on 24.03.2015. */ public abstract class EditUserInfoActivity extends ChiefActivity implements ChiefActivity.CoreServiceListener { private static final int PICK_AVATAR_REQUEST_CODE = 0x01; private static final int CROP_AVATAR_REQUEST_CODE = 0x02; private static final int LARGE_AVATAR_SIZE = 600; private static final String ACTION_CROP = "com.android.camera.action.CROP"; public static final String ACCOUNT_DB_ID = "account_db_id"; public static final String ACCOUNT_TYPE = "account_type"; public static final String AVATAR_HASH = "buddy_avatar_hash"; public static final String USER_NICK = "user_nick"; public static final String FIRST_NAME = "first_name"; public static final String LAST_NAME = "last_name"; private int accountDbId; private String accountType; private String avatarHash; private Bitmap manualAvatar; private String manualAvatarVirtualHash; private boolean isInfoReceived; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Logger.log("AbstractInfoActivity onCreate"); // We want to receive service state notifications. addCoreServiceListener(this); // Obtain and check basic info about interested buddy. Intent intent = getIntent(); accountDbId = intent.getIntExtra(ACCOUNT_DB_ID, GlobalProvider.ROW_INVALID); accountType = intent.getStringExtra(ACCOUNT_TYPE); avatarHash = intent.getStringExtra(AVATAR_HASH); // Check for required fields are correct. if (accountDbId == GlobalProvider.ROW_INVALID) { // Nothing we can do in this case. Only show toast and close activity. onUserInfoRequestError(); setResult(RESULT_CANCELED); finish(); return; } // Initialize info activity layout. setContentView(getLayout()); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); // Preparing for action bar. ActionBar bar = getSupportActionBar(); if (bar != null) { bar.setDisplayShowTitleEnabled(true); bar.setDisplayHomeAsUpEnabled(true); bar.setTitle(R.string.account_info); } // Buddy avatar. ContactImage contactBadge = (ContactImage) findViewById(R.id.buddy_image); BitmapCache.getInstance().getBitmapAsync(contactBadge, avatarHash, R.drawable.def_avatar_0, false); contactBadge.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { View container = findViewById(R.id.buddy_image_container); int targetHeight; if (container.getWidth() != container.getHeight()) { targetHeight = container.getWidth(); } else { targetHeight = (int) getResources().getDimension(R.dimen.buddy_info_avatar_height); } ResizeAnimation resizeAnimation = new ResizeAnimation(container, targetHeight); resizeAnimation.setInterpolator(new OvershootInterpolator()); resizeAnimation.setDuration(500); container.startAnimation(resizeAnimation); } }); View changeAvatarButton = findViewById(R.id.change_avatar_button); changeAvatarButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { pickAvatar(); } }); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { FrameLayout.LayoutParams p; p = (FrameLayout.LayoutParams) changeAvatarButton.getLayoutParams(); p.setMargins(0, 0, 0, 0); changeAvatarButton.setLayoutParams(p); } afterCreate(); } @Override public void onBackPressed() { onActivityCloseAttempt(); } private void onActivityCloseAttempt() { // Check for info received and we must notify user about closing activity. if (isInfoReceived) { new AlertDialog.Builder(this) .setMessage(R.string.close_without_user_info_save) .setNegativeButton(R.string.yes, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }) .setPositiveButton(R.string.no, null) .show(); } else { finish(); } } @Override public boolean onCreateOptionsMenu(final Menu menu) { getMenuInflater().inflate(R.menu.edit_user_info_menu, menu); final MenuItem item = menu.findItem(R.id.edit_user_info_complete); TextView actionView = ((TextView) item.getActionView()); actionView.setText(actionView.getText().toString().toUpperCase()); actionView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { menu.performIdentifierAction(item.getItemId(), 0); } }); if (isInfoReceived) { item.setVisible(true); } else { item.setVisible(false); } return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: setResult(RESULT_CANCELED); onBackPressed(); return true; case R.id.edit_user_info_complete: UpdateAvatarTask.UpdateAvatarCallback callback = new UpdateAvatarTask.UpdateAvatarCallback() { @Override public void onUpdateCompleted() { onAvatarUpdateCompleted(); } @Override public void onUpdateFailed() { onAvatarChangeError(); } }; // Check for manual avatar exists. if (manualAvatar != null && !TextUtils.isEmpty(manualAvatarVirtualHash)) { // This will cache avatar with specified hash and also for current account. TaskExecutor.getInstance().execute(new UpdateAvatarTask(this, callback, accountDbId, manualAvatar, manualAvatarVirtualHash, avatarHash)); } else { onAvatarUpdateCompleted(); } return true; } return false; } protected abstract String getUserNick(); protected abstract String getFirstName(); protected abstract String getLastName(); public int getAccountDbId() { return accountDbId; } protected abstract void afterCreate(); protected abstract int getLayout(); protected void onUserInfoRequestError() { Toast.makeText(this, R.string.error_show_account_info, Toast.LENGTH_SHORT).show(); } private void hideProgressBar() { ViewSwitcher viewSwitcher = (ViewSwitcher) findViewById(R.id.content_view_switcher); if (viewSwitcher.getDisplayedChild() == 0) { viewSwitcher.setDisplayedChild(1); } // Now we means that info received and user ready to modify info. if (!isInfoReceived) { isInfoReceived = true; invalidateOptionsMenu(); ActionBar bar = getSupportActionBar(); if (bar != null) { bar.setHomeAsUpIndicator(R.drawable.ic_close); } } } @Override public void onCoreServiceIntent(Intent intent) { boolean isEditInfoRequest = intent.getBooleanExtra(UserInfoRequest.EDIT_INFO_REQUEST, false); if (isEditInfoRequest) { // Check for info present in this intent. boolean isInfoPresent = !intent.getBooleanExtra(UserInfoRequest.NO_INFO_CASE, false); int requestAccountDbId = intent.getIntExtra(UserInfoRequest.ACCOUNT_DB_ID, GlobalProvider.ROW_INVALID); // Checking for avatar info received. String requestAvatarHash = intent.getStringExtra(UserInfoRequest.BUDDY_AVATAR_HASH); // Checking for this is info we need. if (requestAccountDbId == accountDbId) { // Hide the progress bar. hideProgressBar(); // Checking for avatar hash is new and cool. if (!TextUtils.isEmpty(requestAvatarHash) && !TextUtils.equals(requestAvatarHash, avatarHash)) { avatarHash = requestAvatarHash; ContactImage contactBadgeUpdate = (ContactImage) findViewById(R.id.buddy_image_update); Animation fadeIn = new AlphaAnimation(0, 1); fadeIn.setInterpolator(new AccelerateDecelerateInterpolator()); fadeIn.setDuration(750); contactBadgeUpdate.setAnimation(fadeIn); BitmapCache.getInstance().getBitmapAsync(contactBadgeUpdate, avatarHash, R.drawable.def_avatar_0, true); } if (isInfoPresent) { onUserInfoReceived(intent); } else { onUserInfoRequestError(); } } } } protected abstract void onUserInfoReceived(Intent intent); @Override public void onCoreServiceReady() { try { String appSession = getServiceInteraction().getAppSession(); ContentResolver contentResolver = getContentResolver(); // Sending protocol buddy info request. RequestHelper.requestUserInfo(contentResolver, appSession, accountDbId); } catch (Throwable ignored) { hideProgressBar(); onUserInfoRequestError(); } } @Override public void onCoreServiceDown() { } protected abstract void sendManualAvatarRequest(String hash); protected abstract void sendEditUserInfoRequest(); @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case PICK_AVATAR_REQUEST_CODE: { if (resultCode == RESULT_OK) { onAvatarPicked(data.getData()); } break; } case CROP_AVATAR_REQUEST_CODE: { if (resultCode == RESULT_OK) { Uri uri = data.getData(); if (uri == null && !TextUtils.isEmpty(data.getAction())) { uri = Uri.parse(data.getAction()); } if (uri != null) { TaskExecutor.getInstance().execute(new AvatarSamplingTask(this, uri)); } } break; } } } private void pickAvatar() { try { Intent photoPickerIntent = new Intent(Intent.ACTION_PICK); photoPickerIntent.setType("image/*"); startActivityForResult(photoPickerIntent, PICK_AVATAR_REQUEST_CODE); } catch (Throwable ignored) { // No such application?! } } private void onAvatarPicked(Uri uri) { try { Intent intent = new Intent(ACTION_CROP); intent.setData(uri); intent.putExtra("crop", "true"); intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); intent.putExtra("outputX", LARGE_AVATAR_SIZE); intent.putExtra("outputY", LARGE_AVATAR_SIZE); intent.putExtra("noFaceDetection", true); startActivityForResult(intent, CROP_AVATAR_REQUEST_CODE); } catch (Throwable ignored) { // No such application?! TaskExecutor.getInstance().execute(new AvatarSamplingTask(this, uri)); } } @SuppressWarnings("deprecation") private void setManualAvatar(Bitmap bitmap, String hash) { // Apply manual avatar and hash. manualAvatar = bitmap; manualAvatarVirtualHash = hash; // Show new avatar with animation. ContactImage contactBadgeManual = (ContactImage) findViewById(R.id.buddy_image_manual); Animation fadeIn = new AlphaAnimation(0, 1); fadeIn.setInterpolator(new AccelerateDecelerateInterpolator()); fadeIn.setDuration(750); fadeIn.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { ContactImage contactBadgeUpdate = (ContactImage) findViewById(R.id.buddy_image_update); // Duplicate avatar for layer below. contactBadgeUpdate.setBitmap(manualAvatar); } @Override public void onAnimationRepeat(Animation animation) { } }); contactBadgeManual.setAnimation(fadeIn); contactBadgeManual.setBitmap(bitmap); } private void onAvatarChangeError() { Toast.makeText(this, R.string.avatar_changing_error, Toast.LENGTH_SHORT).show(); } private void onAvatarUpdateCompleted() { sendEditUserInfoRequest(); setResult(RESULT_OK, new Intent() .putExtra(USER_NICK, getUserNick()) .putExtra(FIRST_NAME, getFirstName()) .putExtra(LAST_NAME, getLastName()) .putExtra(AVATAR_HASH, manualAvatarVirtualHash)); finish(); } public static class AvatarSamplingTask extends WeakObjectTask<EditUserInfoActivity> { private final Uri uri; private Bitmap bitmap; private String hash; public AvatarSamplingTask(EditUserInfoActivity object, Uri uri) { super(object); this.uri = uri; this.hash = HttpUtil.getUrlHash(uri.toString()); } @Override public void executeBackground() throws Throwable { EditUserInfoActivity activity = getWeakObject(); if (activity != null) { bitmap = BitmapHelper.decodeSampledBitmapFromUri(activity, uri, LARGE_AVATAR_SIZE, LARGE_AVATAR_SIZE); if (bitmap == null) { throw new NullPointerException(); } } } @Override public void onSuccessMain() { EditUserInfoActivity activity = getWeakObject(); if (activity != null && bitmap != null) { activity.setManualAvatar(bitmap, hash); } } @Override public void onFailMain() { EditUserInfoActivity activity = getWeakObject(); if (activity != null) { activity.onAvatarChangeError(); } } } public static class UpdateAvatarTask extends PleaseWaitTask { private final WeakReference<EditUserInfoActivity> weakActivity; private final Bitmap avatar; private final UpdateAvatarCallback callback; private final int accountDbId; private final String virtualHash; private final String avatarHash; public UpdateAvatarTask(EditUserInfoActivity object, UpdateAvatarCallback callback, int accountDbId, Bitmap avatar, String virtualHash, String avatarHash) { super(object); weakActivity = new WeakReference<>(object); this.callback = callback; this.accountDbId = accountDbId; this.avatar = avatar; this.virtualHash = virtualHash; this.avatarHash = TextUtils.isEmpty(avatarHash) ? virtualHash : avatarHash; } @Override public void executeBackground() throws Throwable { EditUserInfoActivity activity = weakActivity.get(); if (activity != null) { BitmapCache bitmapCache = BitmapCache.getInstance(); // Remove all cached avatars. bitmapCache.invalidateHash(virtualHash); bitmapCache.invalidateHash(avatarHash); // Same new bitmaps. bitmapCache.saveBitmapSync(virtualHash, avatar); bitmapCache.saveBitmapSync(avatarHash, avatar); // Update profile. QueryHelper.updateAccountAvatar(activity.getContentResolver(), accountDbId, virtualHash); } } @Override public void onSuccessMain() { EditUserInfoActivity activity = weakActivity.get(); if (activity != null && avatar != null) { activity.sendManualAvatarRequest(avatarHash); callback.onUpdateCompleted(); } } @Override public void onFailMain() { EditUserInfoActivity activity = weakActivity.get(); if (activity != null) { activity.onAvatarChangeError(); callback.onUpdateFailed(); } } public interface UpdateAvatarCallback { void onUpdateCompleted(); void onUpdateFailed(); } } }