Understanding Window#getSharedElementTransition() - android

I have two Activities A and B which have a SharedElement. If Activity A starts Activity B and listens to the the transition, both the listener for exit and reenter are called.
Here the code for the calling Activity A:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getWindow().getSharedElementReenterTransition().addListener(new Transition.TransitionListener() {
#Override
public void onTransitionStart(Transition transition) {
Log.i("Log", "A REENTER");
}
...
});
getWindow().getSharedElementExitTransition().addListener(new Transition.TransitionListener() {
#Override
public void onTransitionStart(Transition transition) {
Log.i("Log", "A EXIT");
}
...
});
getWindow().getSharedElementEnterTransition().addListener(new Transition.TransitionListener() {
#Override
public void onTransitionStart(Transition transition) {
Log.i("TestApp", "A ENTER");
}
...
});
getWindow().getSharedElementReturnTransition().addListener(new Transition.TransitionListener() {
#Override
public void onTransitionStart(Transition transition) {
Log.i("TestApp", "A RETURN");
}
...
});
}
public void onClick(View v){
Intent intent = new Intent(this, Act2.class);
Pair<View, String> pair1 = Pair.create(findViewById(R.id.textView), findViewById(R.id.textView).getTransitionName());
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(this, pair1);
startActivity(intent, options.toBundle());
}
}
If I now execute onClick() (to start Activity B) and then hit the back button to return to Activity A, the Log will be as follows:
A REENTER
A EXIT
B ENTER
B RETURN
B ENTER
B RETURN
A REENTER
A EXIT
I would expect it to be
A EXIT
B ENTER
B RETURN
A REENTER

By default, the same transition is used for both the exit and reenter transitions as well as the enter and return transitions. If you explicitly set them, they will be different.
I believe that you are adding listeners to the same transition, so they are all being called.

I ran into the similar problem and found a similar question
There is a bug in Lollipop that causes the shared element return transition to be interrupted if it takes longer than the reenter
transition duration. If you adjust your reenter transition duration
(on the calling Activity), that should fix the interruption problem.
You better use the enter and return shared element transitions.

Related

Callback when task goes into background or comes into foreground?

I have an activity A, it launches custom-tab. I need to know while the custom tab is open, if the task (of which the activity is part of) goes to background or comes to foreground.
I am aware of this question How to detect when an Android app goes to the background and come back to the foreground . The solutions mentioned for this question don't work for me because as soon as custom tab is launched, the onbackground callback is received, which is not what I want. I want onbackground callback, when the task containing the activity A goes to background.
Using the CustomTabsCallback you can listen when the Tab becomes hidden (goes into background) using the TAB_HIDDEN callback or TAB_SHOWN callback when the Tab becomes visible (goes into foreground).
From the Documentation:
TAB_HIDDEN
Sent when the tab becomes hidden.
TAB_SHOWN
Sent when the tab becomes visible.
Below is a full working example of how you can use the above callbacks:
public class CustomTabsActivity extends AppCompatActivity {
private CustomTabsServiceConnection mCustomTabsServiceConnection;
private CustomTabsClient mCustomTabsClient;
private CustomTabsSession mCustomTabsSession;
private CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.customTabsButton).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
showCustomTabs();
}
});
initCustomTabs();
}
#Override
protected void onStart() {
super.onStart();
CustomTabsClient.bindCustomTabsService(this, "com.android.chrome", mCustomTabsServiceConnection);
}
#Override
protected void onResume() {
super.onResume();
}
#Override
protected void onStop() {
super.onStop();
}
#Override
protected void onDestroy() {
super.onDestroy();
}
private void initCustomTabs() {
mCustomTabsServiceConnection = new CustomTabsServiceConnection()
{
#Override
public void onCustomTabsServiceConnected(#NotNull ComponentName componentName, #NotNull CustomTabsClient customTabsClient)
{
mCustomTabsClient = customTabsClient;
mCustomTabsClient.warmup(0L);
mCustomTabsSession = mCustomTabsClient.newSession(new CustomTabsCallback()
{
#Override
public void onNavigationEvent(int navigationEvent, Bundle extras) {
switch (navigationEvent)
{
case CustomTabsCallback.TAB_SHOWN:
//Sent when the tab becomes visible (goes into foreground)
break;
case CustomTabsCallback.TAB_HIDDEN:
//Sent when the tab becomes hidden (goes into background)
break;
}
}
});
builder.setSession(mCustomTabsSession);
}
#Override
public void onServiceDisconnected(ComponentName name) {
mCustomTabsClient = null;
}
};
CustomTabsClient.bindCustomTabsService(this, "com.android.chrome", mCustomTabsServiceConnection);
}
private void showCustomTabs(){
builder.setShowTitle(true);
CustomTabsIntent customTabsIntent = builder.build();
customTabsIntent.launchUrl(this, Uri.parse("https://stackoverflow.com/"));
}
}
The relationship between your activity and chrome custom tabs depends on the launchMode. You can launch the custom tab in current stack or a new stack.

Memory leak using Lollipop's transitions

I'm trying to implement transition between fragment (that attached to the MainActivity) with RecyclerView and the DetailActivity.
In my fragment, I've added RecyclerView listener in onStart() method:
#Override
public void onStart() {
super.onStart();
mRecyclerItemClickListener = new RecyclerItemClickListener(getActivity(), (view, position) -> {
Intent intent = new Intent(getActivity(), DetailActivity.class);
intent.putExtra(ConstantsManager.POSITION_ID_KEY, mFilmsAdapter.getImdbIdByPosition(position));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
ActivityOptionsCompat optionsCompat = ActivityOptionsCompat
.makeSceneTransitionAnimation(getActivity());
ActivityCompat.startActivity(getActivity(), intent, optionsCompat.toBundle());
} else {
startActivity(intent);
}
});
mRecyclerView.addOnItemTouchListener(mRecyclerItemClickListener);
mSwipeRefreshLayout.setOnRefreshListener(this);
mTopFilmsPresenter.attachView(this);
mTopFilmsPresenter.getFilms();
}
You can see, that the transition begins in the if block. In the DetailActivity I have the following method which I call in onCreate():
#TargetApi(Build.VERSION_CODES.KITKAT)
private void setupWindowAnimations() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Explode explode = new Explode();
explode.setDuration(ConstantsManager.TRANSITION_DURATION);
getWindow().setEnterTransition(explode);
getWindow().setExitTransition(explode);
}
}
And in the MainActivity I have almost a similar method, which I also call in onCreate():
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void setWindowAnimations() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Explode explode = new Explode();
explode.setDuration(ConstantsManager.TRANSITION_DURATION);
getWindow().setEnterTransition(explode);
getWindow().setExitTransition(explode);
getWindow().setReenterTransition(explode);
getWindow().setReturnTransition(explode);
}
}
I've also implemented Transition.TransitionListener interface in the DetailActivity, because documentation says:
A transition listener receives notifications from a transition. Notifications indicate transition lifecycle events.
So I'm trying to remove listener in the onTransitionEnd(Transition transition), onTransitionCancel(Transition transition) and onTransitionPause(Transition transition) callbacks:
#Override
public void onTransitionStart(Transition transition) {
}
#TargetApi(Build.VERSION_CODES.KITKAT)
#Override
public void onTransitionEnd(Transition transition) {
transition.removeListener(this);
}
#TargetApi(Build.VERSION_CODES.KITKAT)
#Override
public void onTransitionCancel(Transition transition) {
transition.removeListener(this);
}
#TargetApi(Build.VERSION_CODES.KITKAT)
#Override
public void onTransitionPause(Transition transition) {
transition.removeListener(this);
}
#Override
public void onTransitionResume(Transition transition) {
}
I'm using LeakCanary for memory leaks detections and it detects a memory leak after transition:
So I am wondering how can I remove transition listener(s) to prevent this memory leak?
Ensure that you release the Activity reference since you are strongly holding onto it within the Transition class. A simple Transition.removeListener(this) (since your activity implements the interface) in it's onDestroy() method should prevent memory leaks.
Thanks Albert Vila for the answer. The problem lies in TransitionManager.sRunningTransitions according to this topic.

how to override onBackPressed inside an if statement?

My aim is to override the code of my onBackPressed method..
I have overridden the onBackPressed inside my activity
#Override
public void onBackPressed() {
this.finish();
}
The code below is inside onCreate(), how can i override the onBackPressed to do something like go to another intent instead,
if(mode.equals("edit")){
//onBackPressed();
}
EDIT!!
sorry for unclear question,
What i want to know is, is there a way to override the method inside to onCreate method's if statement?
Just do this:
#Override
public void onBackPressed() {
// Check your mode in onBackPressed
if(mode.equals("edit")){
// Launch the intent
Intent editIntent = new Intent(MyActivity.this, EditActivity.class);
startActivity(editIntent);
// else call to the super class method, for default behavior
}else{
super.onBackPressed();
}
}
There is nothing stopping you from making an onBackPressed call from any method in your Activity class:
public class MainActivity extends AppCompatActivity {
private int editMode = 1;
private String mode = "edit";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// This is totally legal to do
if(editMode == 1){
onBackPressed();
}
}
}
Since the method onBackPressed() is a public method You can do this:
inside onCreate(){
if(mode.equals("edit")){
onBackPressed();
}
else
Log.d("dj","something else");
}
in onBackPressed, i did this for testing:
#Override
public void onBackPressed() {
Log.d("dj", "Yeah! on back pressed");
//super.onBackPressed();
}
I think you want to perform some operation based on the calling location,
for that I think you can follow the given below method:
onCreate(){
if(Edit){
performOperation(EDIT_OPERATION);
}
if(View){
performOperation(VIEW_OPERATION);
}
}
onBackPressed(){
performOperation(BACKP_RESSED);
}
performOperation(int operationType){
/*
* do operation
*/
}
I hope this is what you want

exit() and killProcess() doesn't work

I'm trying to close my app when back button is pressed. So I overridden onBackPressed() in my activity:
public void onBackPressed()
{
Process.killProcess(Process.myPid());
}
I also tried:
public void onBackPressed()
{
exit(0);
}
my app got 3 tasks and 5 activities: A, B, C, D and E. Where I placed A, B and C in one task While D and E got their own tasks. I want to exit the app when the back button is pressed in activities A, D and E.
I try toasting from onBackPressed() and function callback works fine.
When I click the back button on my device in activities A or D or E it just goes to the previous activity from another task !!
public class CloseApplication
{
private static boolean isToCloseApp = false;
public static boolean isToCloseApp()
{
return isToCloseApp;
}
public static void updateFlagToCloseTheApp(boolean flag)
{
isToCloseApp = flag;
}
}
onBackKey Press UseCode
public void onBackPressed()
{
CloseApplication.updateFlagToCloseTheApp(true);
finish();
}
One more Thing in each activity of your application Override the onResume method.
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
if(CloseApplication.isToCloseApp())
{
finish();
}
}
mark the answer solve if it solve your problem and so that it helps the other to find the answer.

Free up memory on Android when push Back Button

I have 2 activities, Activity1 and Activity2. In Activity1 I have a button to go to Activity2.
Activity2 has a lot of Edittext and other Views.
When I start the app, the memory of the process is 10mb. If I click on the button and Activity2 is loaded, my memory's process is about 59mb.
The issue is, in Activity2, if I push Back Button, I return to Activity1 and my memory's process is about 59mb, and I don't need this information about Activity2.
Now, If I click again the button, I have an OutOfMemory.
How can I force to free up the memory when I push Back Button?
I try to call finish() and System.gc() but It doesn't work.
Thank you
try this one....
first close your activity...
use following code...
public class ur_clss extends Activity {
private ur_class c1;
//ur content here
#Override
public void onBackPressed () {
c1.finishActivity(0);
}
}
You need to override the BackButton and free up the memory when it is pressed.
#Override
public void onBackPressed()
{
Activity.finish() // the activity that you want to terminate
}
Or, there is another way to do it. When you start the new activity, the old activity goes onPause(). You could try calling finish() in the onPause() of the old activity.
This is the structure of the code:
public class Activity2 extends Activity {
// Global variables
private CarregaOperacions carrega_operacions;
private TableLayout taula;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Some code of UI
// Show a ProgressBar
loadProgress();
carrega_operacions = new CarregaOperacions(Activity2.this);
carrega_operacions.execute(null);
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
if ((keyCode == KeyEvent.KEYCODE_BACK))
{
finish();
}
return super.onKeyDown(keyCode, event);
}
static class CarregaOperacions extends AsyncTask<Void, Void, Void> {
WeakReference<Activity2> context;
Activity2 act;
public CarregaOperacions(Activity2 activity) {
context = new WeakReference<Activity2>(activity);
act = context.get();
}
#Override
protected Void doInBackground(Void... arg0) {
act.carregaOperacions();
return null;
}
protected void onPostExecute(Void result) {
ArrayList<LinearLayout> llista = act.getLlistaFiles();
for (int i = 0; i < llista.size(); i++ ) {
act.getTable().addView(llista.get(i));
}
act.getScroll().setVisibility(View.VISIBLE);
act.treuProgres();
}
}
With this code, I explain a bit:
The real case is, Activity1 calls a TabActivity, that has the Activity2. Activity2 loads a lot of registers, and I do this asynchronously. I Override onKeyDown in Activity2 and on the TabActivity, but it seems that only is onKeyDown's Activity2 executed.

Categories

Resources