Skip to content

Instantly share code, notes, and snippets.

@monzee
Created May 22, 2017 02:51
Show Gist options
  • Save monzee/976393c8097c2496d701109fdcba491f to your computer and use it in GitHub Desktop.
Save monzee/976393c8097c2496d701109fdcba491f to your computer and use it in GitHub Desktop.
Java vs. Kotlin
public class MainActivity extends AppCompatActivity {
static class Retained {
final LipsumAdapter adapter = new LipsumAdapter();
State loadState = State.Case::unknown;
}
interface State {
void match(Case of);
interface Case {
void unknown();
void loading(Future<State> result);
void loaded(List<String> items);
void failed(Exception error);
}
}
static class LipsumAdapter extends RecyclerView.Adapter<LipsumAdapter.RowView> {
static class RowView extends RecyclerView.ViewHolder {
final TextView root;
RowView(View itemView) {
super(itemView);
itemView.setClickable(true);
root = (TextView) itemView;
}
void show(String item) {
root.setText(item);
}
}
private List<String> items;
@Override
public RowView onCreateViewHolder(ViewGroup parent, int viewType) {
return new RowView(LayoutInflater
.from(parent.getContext())
.inflate(android.R.layout.simple_selectable_list_item, parent, false));
}
@Override
public void onBindViewHolder(RowView holder, int position) {
holder.show(items.get(position));
}
@Override
public int getItemCount() {
return items.size();
}
public void setItems(List<String> items) {
this.items = items;
notifyDataSetChanged();
}
}
private static final ExecutorService BACKGROUND = Executors.newCachedThreadPool();
private Retained scope;
private RecyclerView theItems;
private ProgressBar theSpinner;
private Future<?> waiting;
@Override
protected void onCreate(Bundle savedInstanceState) {
scope = (Retained) getLastCustomNonConfigurationInstance();
if (scope == null) {
scope = new Retained();
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
theSpinner = (ProgressBar) findViewById(R.id.the_spinner);
theItems = (RecyclerView) findViewById(R.id.the_items);
theItems.setAdapter(scope.adapter);
theItems.setLayoutManager(new LinearLayoutManager(this));
}
@Override
public Object onRetainCustomNonConfigurationInstance() {
return scope;
}
@Override
protected void onResume() {
super.onResume();
update();
}
@Override
protected void onPause() {
super.onPause();
if (waiting != null) {
waiting.cancel(true);
waiting = null;
}
}
void update(State newState) {
scope.loadState = newState;
update();
}
void update() {
scope.loadState.match(new State.Case() {
@Override
public void unknown() {
Future<State> result = BACKGROUND.submit(MainActivity::loadData);
update(of -> of.loading(result));
}
@Override
public void loading(Future<State> result) {
showProgress(true);
waiting = BACKGROUND.submit(() -> {
State nextState = scope.loadState;
try {
nextState = result.get();
} catch (ExecutionException e) {
nextState = of -> of.failed(e);
} catch (InterruptedException e) {
runOnUiThread(() -> Toast
.makeText(getApplicationContext(), "wait cancelled", Toast.LENGTH_SHORT)
.show());
} finally {
State newState = nextState;
runOnUiThread(() -> update(newState));
}
});
}
@Override
public void loaded(List<String> items) {
showProgress(false);
waiting = null;
scope.adapter.setItems(items);
}
@Override
public void failed(Exception error) {
throw new RuntimeException(error);
}
});
}
void showProgress(boolean busy) {
if (busy) {
theSpinner.setVisibility(View.VISIBLE);
theItems.setVisibility(View.GONE);
} else {
theSpinner.setVisibility(View.GONE);
theItems.setVisibility(View.VISIBLE);
}
}
static State loadData() throws InterruptedException {
Thread.sleep(10_000);
List<String> items = Arrays.asList(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur malesuada id tortor sit amet scelerisque.",
"Integer facilisis sem et sapien tempor, vel semper risus ultrices. Aenean molestie turpis ipsum, id ultricies tortor dictum a.",
"Pellentesque et eleifend libero. Phasellus vestibulum consectetur arcu, id tincidunt magna luctus sit amet.",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur malesuada id tortor sit amet scelerisque.",
"Integer facilisis sem et sapien tempor, vel semper risus ultrices. Aenean molestie turpis ipsum, id ultricies tortor dictum a.",
"Pellentesque et eleifend libero. Phasellus vestibulum consectetur arcu, id tincidunt magna luctus sit amet.",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur malesuada id tortor sit amet scelerisque.",
"Integer facilisis sem et sapien tempor, vel semper risus ultrices. Aenean molestie turpis ipsum, id ultricies tortor dictum a.",
"Pellentesque et eleifend libero. Phasellus vestibulum consectetur arcu, id tincidunt magna luctus sit amet.",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur malesuada id tortor sit amet scelerisque.",
"Integer facilisis sem et sapien tempor, vel semper risus ultrices. Aenean molestie turpis ipsum, id ultricies tortor dictum a.",
"Pellentesque et eleifend libero. Phasellus vestibulum consectetur arcu, id tincidunt magna luctus sit amet.");
return of -> of.loaded(items);
}
}
class MainActivity : AppCompatActivity() {
internal val adapter = LipsumAdapter()
internal lateinit var my: Scope
internal lateinit var theItems: RecyclerView
internal var pending: Future<*>? = null
override fun onCreate(savedInstanceState: Bundle?) {
my = lastCustomNonConfigurationInstance as? Scope ?: Scope()
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_coordinator_test)
theItems = (findViewById(R.id.the_items) as RecyclerView).apply {
adapter = this@MainActivity.adapter
layoutManager = LinearLayoutManager(context)
addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL))
}
}
override fun onRetainCustomNonConfigurationInstance(): Any = my
override fun onResume() {
super.onResume()
update(my.state)
}
override fun onPause() {
super.onPause()
pending?.cancel(true)
}
internal fun update(next: State): Unit = runOnUiThread {
my.state = next
when (next) {
is Unknown -> update(Loading(my.io.submit(::loadData)))
is Loading -> {
pending = my.io.submit {
try {
update(next.task.get())
} catch (e: InterruptedException) {
} catch (e: ExecutionException) {
update(Error(e))
} finally {
pending = null
}
}
}
is Loaded -> adapter.items = next.items
is Error -> error(next.e)
}
}
}
internal class Scope {
val io: ExecutorService = Executors.newCachedThreadPool()
var state: State = Unknown
}
sealed class State
object Unknown : State()
data class Loading(val task: Future<State>) : State()
data class Loaded(val items: List<String>) : State()
data class Error(val e: Throwable) : State()
internal fun loadData(): State {
Thread.sleep(10_000)
return Loaded(listOf(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur malesuada id tortor sit amet scelerisque.",
"Integer facilisis sem et sapien tempor, vel semper risus ultrices. Aenean molestie turpis ipsum, id ultricies tortor dictum a.",
"Pellentesque et eleifend libero. Phasellus vestibulum consectetur arcu, id tincidunt magna luctus sit amet.",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur malesuada id tortor sit amet scelerisque.",
"Integer facilisis sem et sapien tempor, vel semper risus ultrices. Aenean molestie turpis ipsum, id ultricies tortor dictum a.",
"Pellentesque et eleifend libero. Phasellus vestibulum consectetur arcu, id tincidunt magna luctus sit amet.",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur malesuada id tortor sit amet scelerisque.",
"Integer facilisis sem et sapien tempor, vel semper risus ultrices. Aenean molestie turpis ipsum, id ultricies tortor dictum a.",
"Pellentesque et eleifend libero. Phasellus vestibulum consectetur arcu, id tincidunt magna luctus sit amet.",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur malesuada id tortor sit amet scelerisque.",
"Integer facilisis sem et sapien tempor, vel semper risus ultrices. Aenean molestie turpis ipsum, id ultricies tortor dictum a.",
"Pellentesque et eleifend libero. Phasellus vestibulum consectetur arcu, id tincidunt magna luctus sit amet."))
}
internal class LipsumAdapter : RecyclerView.Adapter<LipsumAdapter.RowView>() {
internal class RowView(itemView: View) : RecyclerView.ViewHolder(itemView) {
val root: TextView = itemView as TextView
init {
itemView.isClickable = true
}
fun show(item: String) {
root.text = item
}
}
internal var items: List<String> = Collections.emptyList()
set(value) = notifyDataSetChanged()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = LayoutInflater
.from(parent.context)
.inflate(android.R.layout.simple_selectable_list_item, parent, false)
.let(::RowView)
override fun onBindViewHolder(holder: RowView, position: Int) = holder.show(items[position])
override fun getItemCount(): Int = items.size
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment