Skip to content

Commit

Permalink
share backstack via Activity.getSystemService(), share key via Contex…
Browse files Browse the repository at this point in the history
…tWrapper.getSystemService()
  • Loading branch information
Zhuinden committed Jan 13, 2017
1 parent 7e2ecf5 commit 055b79f
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 72 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Change log

Simple Stack 0.1.1 (2017-01-14)
---------------------------------
- Key and backstack are now provided to custom viewgroup via `getSystemService()`

Simple Stack 0.1.0 (2017-01-13)
---------------------------------
- Added initial `Backstack`, `StateChange` and `StateChanger` classes.
- Backstack allows manipulation of state via `goTo()`, `goBack()` and `setHistory()`.
- Demo persists backstack history through config change and process death.

Limitations:
- ViewState is not persisted
- scheduling state changes (starting a state change while another is in progress) is not allowed
- there is a possibility that state change can occur even after `onPause()`
- key and backstack are manually set to the custom viewgroup, which means these are not directly accessible in their child views (and the interfaces are ugly anyways)
39 changes: 20 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ Afterwards, the backstack operators allow changing between states.

``` java
public class MainActivity extends AppCompatActivity implements StateChanger {
public static final String BACKSTACK = "BACKSTACK";

@BindView(R.id.root)
RelativeLayout root;

Expand All @@ -48,31 +50,29 @@ public class MainActivity extends AppCompatActivity implements StateChanger {
setContentView(R.layout.activity_main);
ButterKnife.bind(this);

// initialize backstack from persisted state if exists, otherwise with new elements.
ArrayList<Parcelable> keys;
if(savedInstanceState != null) {
keys = savedInstanceState.getParcelableArrayList("BACKSTACK");
keys = savedInstanceState.getParcelableArrayList(BACKSTACK);
} else {
keys = new ArrayList<>();
keys.add(new FirstKey());

}
backstack = (Backstack)getLastCustomNonConfigurationInstance();
if(backstack == null) {
backstack = new Backstack(keys);
}

// set this as state changer, and handle initialization
backstack.setStateChanger(this);
}

@Override
public Object onRetainCustomNonConfigurationInstance() { // persist stack across config change
public Object onRetainCustomNonConfigurationInstance() {
return backstack;
}

@Override
public void onBackPressed() {
if(!backstack.goBack()) { // handle back press
if(!backstack.goBack()) {
super.onBackPressed();
}
}
Expand All @@ -82,12 +82,12 @@ public class MainActivity extends AppCompatActivity implements StateChanger {
super.onSaveInstanceState(outState);
ArrayList<Parcelable> history = new ArrayList<>();
history.addAll(backstack.getHistory());
outState.putParcelableArrayList("BACKSTACK", history); // persist state of backstack across config change/process death
outState.putParcelableArrayList(BACKSTACK, history);
}

@Override
protected void onDestroy() {
backstack.removeStateChanger(); // backstack survives config change, so Activity should remove its reference
backstack.removeStateChanger();
super.onDestroy();
}

Expand All @@ -98,16 +98,21 @@ public class MainActivity extends AppCompatActivity implements StateChanger {
completionCallback.stateChangeComplete();
return;
}
root.removeAllViews(); // no state persistence for previous views yet
root.removeAllViews();
Key newKey = stateChange.topNewState();
View view = LayoutInflater.from(this).inflate(newKey.layout(), root, false);
BackstackHolder backstackHolder = (BackstackHolder)view;
backstackHolder.setBackstack(backstack); // view currently doesn't implicitly receive the backstack
KeyHolder keyHolder = (KeyHolder)view;
keyHolder.setKey(newKey); // view currently doesn't implicitly receive the key
Context newContext = new KeyContextWrapper(this, newKey);
View view = LayoutInflater.from(newContext).inflate(newKey.layout(), root, false);
root.addView(view);
completionCallback.stateChangeComplete();
}

@Override
public Object getSystemService(String name) {
if(BACKSTACK.equals(name)) {
return backstack;
}
return super.getSystemService(name);
}
}
```

Expand All @@ -117,8 +122,4 @@ Currently, the backstack does **not** do viewstate persistence, only stores the

Scheduling a state change while a state change is already in progress throws an `IllegalStateException` instead of queueing it.

## Demo limitations

In the demo, keys associated with the given custom views are manually set for the custom views, therefore it is set via an interface, but is not accessible for any child views of the custom viewgroup.

In the demo, the backstack is also manually set to the custom viewgroup using a `BackStackHolder` interface, instead of implicitly providing it via the Context.
It is possible to start a state change even after `onPause()` instead of queueing it.
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
package com.zhuinden.simplestackdemoexample;

import android.content.Context;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.RelativeLayout;

import com.zhuinden.simplestackdemo.stack.Backstack;
import com.zhuinden.simplestackdemo.stack.StateChange;
import com.zhuinden.simplestackdemo.stack.StateChanger;
import com.zhuinden.simplestackdemoexample.demo.BackstackHolder;
import com.zhuinden.simplestackdemoexample.demo.FirstKey;
import com.zhuinden.simplestackdemoexample.demo.Key;
import com.zhuinden.simplestackdemoexample.R;
import com.zhuinden.simplestackdemoexample.demo.KeyHolder;
import com.zhuinden.simplestackdemoexample.demo.KeyContextWrapper;

import java.util.ArrayList;

import butterknife.BindView;
import butterknife.ButterKnife;

public class MainActivity extends AppCompatActivity implements StateChanger {
public static final String BACKSTACK = "BACKSTACK";

@BindView(R.id.root)
RelativeLayout root;

Expand All @@ -33,7 +36,7 @@ protected void onCreate(Bundle savedInstanceState) {

ArrayList<Parcelable> keys;
if(savedInstanceState != null) {
keys = savedInstanceState.getParcelableArrayList("BACKSTACK");
keys = savedInstanceState.getParcelableArrayList(BACKSTACK);
} else {
keys = new ArrayList<>();
keys.add(new FirstKey());
Expand Down Expand Up @@ -62,7 +65,7 @@ protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
ArrayList<Parcelable> history = new ArrayList<>();
history.addAll(backstack.getHistory());
outState.putParcelableArrayList("BACKSTACK", history);
outState.putParcelableArrayList(BACKSTACK, history);
}

@Override
Expand All @@ -80,12 +83,17 @@ public void handleStateChange(StateChange stateChange, Callback completionCallba
}
root.removeAllViews();
Key newKey = stateChange.topNewState();
View view = LayoutInflater.from(this).inflate(newKey.layout(), root, false);
BackstackHolder backstackHolder = (BackstackHolder)view;
backstackHolder.setBackstack(backstack);
KeyHolder keyHolder = (KeyHolder)view;
keyHolder.setKey(newKey);
Context newContext = new KeyContextWrapper(this, newKey);
View view = LayoutInflater.from(newContext).inflate(newKey.layout(), root, false);
root.addView(view);
completionCallback.stateChangeComplete();
}

@Override
public Object getSystemService(String name) {
if(BACKSTACK.equals(name)) {
return backstack;
}
return super.getSystemService(name);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,52 @@
import android.util.AttributeSet;
import android.view.View;
import android.widget.RelativeLayout;

import com.zhuinden.simplestackdemo.stack.Backstack;
import com.zhuinden.simplestackdemoexample.MainActivity;
import com.zhuinden.simplestackdemoexample.R;

import butterknife.ButterKnife;
import butterknife.OnClick;
/**
* Created by Owner on 2017. 01. 12..
*/

public class FirstView extends RelativeLayout implements BackstackHolder, KeyHolder {
public class FirstView
extends RelativeLayout {
public FirstView(Context context) {
super(context);
init(context);
}

public FirstView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}

public FirstView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}

@TargetApi(21)
public FirstView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context);
}

Backstack backstack;

FirstKey firstKey;

private void init(Context context) {
if(!isInEditMode()) {
// noinspection ResourceType
backstack = (Backstack) context.getSystemService(MainActivity.BACKSTACK);
firstKey = KeyContextWrapper.getKey(context);
}
}

@OnClick(R.id.first_button)
public void clickButton(View view) {
backstack.goTo(new SecondKey());
Expand All @@ -45,14 +61,4 @@ protected void onFinishInflate() {
super.onFinishInflate();
ButterKnife.bind(this);
}

@Override
public void setBackstack(Backstack backstack) {
this.backstack = backstack;
}

@Override
public void setKey(Key key) {
this.firstKey = (FirstKey)key;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.zhuinden.simplestackdemoexample.demo;

import android.content.Context;
import android.content.ContextWrapper;
import android.os.Parcelable;
import android.view.LayoutInflater;

/**
* Created by Zhuinden on 2017.01.14..
*/

public class KeyContextWrapper
extends ContextWrapper {
public static final String KEY = "KEY";

LayoutInflater layoutInflater;

final Parcelable key;

public KeyContextWrapper(Context base, Parcelable key) {
super(base);
this.key = key;
}

@Override
public Object getSystemService(String name) {
if(Context.LAYOUT_INFLATER_SERVICE.equals(name)) {
if(layoutInflater == null) {
layoutInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
}
return layoutInflater;
} else if(KEY.equals(name)) {
return key;
}
return super.getSystemService(name);
}


public static <T extends Parcelable> T getKey(Context context) {
// noinspection ResourceType
Object key = context.getSystemService(KEY);
// noinspection unchecked
return (T) key;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,46 @@
import android.content.Context;
import android.util.AttributeSet;
import android.widget.RelativeLayout;

import com.zhuinden.simplestackdemo.stack.Backstack;
import com.zhuinden.simplestackdemoexample.MainActivity;

/**
* Created by Owner on 2017. 01. 12..
*/

public class SecondView extends RelativeLayout implements BackstackHolder, KeyHolder {
public class SecondView
extends RelativeLayout {
public SecondView(Context context) {
super(context);
init(context);
}

public SecondView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}

public SecondView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}

@TargetApi(21)
public SecondView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context);
}

private void init(Context context) {
if(!isInEditMode()) {
// noinspection ResourceType
backstack = (Backstack) context.getSystemService(MainActivity.BACKSTACK);
secondKey = KeyContextWrapper.getKey(context);
}
}

Backstack backstack;

SecondKey secondKey;

@Override
public void setBackstack(Backstack backstack) {
this.backstack = backstack;
}

@Override
public void setKey(Key key) {
this.secondKey = (SecondKey)key;
}
}

0 comments on commit 055b79f

Please sign in to comment.