Commit 0a283c66 authored by Mark Murphy's avatar Mark Murphy

new samples, update to AS 3.3.1

parent f420b963
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.commonsware.jetpack.samplerj.fragments"
minSdkVersion 21
targetSdkVersion 28
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
dataBinding {
enabled = true
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.recyclerview:recyclerview:1.0.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
}
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.commonsware.jetpack.samplerj.fragments"
xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
\ No newline at end of file
/*
Copyright (c) 2019 CommonsWare, LLC
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required
by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, either express or implied. See the License for the specific
language governing permissions and limitations under the License.
Covered in detail in the book _Elements of Android Jetpack_
https://commonsware.com/Jetpack
*/
package com.commonsware.jetpack.samplerj.fragments;
import android.os.Bundle;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.commonsware.jetpack.samplerj.fragments.databinding.TodoDisplayBinding;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProviders;
public class DisplayFragment extends Fragment {
private static final String ARG_MODEL_ID = "modelId";
private TodoDisplayBinding binding;
static DisplayFragment newInstance(String modelId) {
DisplayFragment result = new DisplayFragment();
Bundle args = new Bundle();
args.putString(ARG_MODEL_ID, modelId);
result.setArguments(args);
return result;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = TodoDisplayBinding.inflate(inflater, container, false);
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view,
@Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
DisplayViewModel vm =
ViewModelProviders.of(this).get(DisplayViewModel.class);
String modelId = getArguments().getString(ARG_MODEL_ID);
if (modelId == null) {
throw new IllegalArgumentException("no modelId provided!");
}
ToDoModel model = vm.getModel(modelId);
if (model != null) {
binding.setModel(model);
binding.setCreatedOnFormatted(DateUtils.getRelativeDateTimeString(
getActivity(),
model.createdOn.getTimeInMillis(), DateUtils.MINUTE_IN_MILLIS,
DateUtils.WEEK_IN_MILLIS, 0
));
}
}
}
/*
Copyright (c) 2019 CommonsWare, LLC
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required
by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, either express or implied. See the License for the specific
language governing permissions and limitations under the License.
Covered in detail in the book _Elements of Android Jetpack_
https://commonsware.com/Jetpack
*/
package com.commonsware.jetpack.samplerj.fragments;
import androidx.lifecycle.ViewModel;
public class DisplayViewModel extends ViewModel {
private ToDoModel model;
ToDoModel getModel(String id) {
if (model == null) {
model = ToDoRepository.INSTANCE.findItemById(id);
}
return model;
}
}
/*
Copyright (c) 2019 CommonsWare, LLC
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required
by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, either express or implied. See the License for the specific
language governing permissions and limitations under the License.
Covered in detail in the book _Elements of Android Jetpack_
https://commonsware.com/Jetpack
*/
package com.commonsware.jetpack.samplerj.fragments;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProviders;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
public class ListFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.todo_roster, container, false);
}
@Override
public void onViewCreated(@NonNull View view,
@Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ListViewModel vm = ViewModelProviders.of(this).get(ListViewModel.class);
RecyclerView rv = view.findViewById(R.id.items);
rv.setLayoutManager(new LinearLayoutManager(getContext()));
ToDoListAdapter adapter = new ToDoListAdapter(getLayoutInflater(),
this::navTo);
adapter.submitList(vm.items);
rv.setAdapter(adapter);
}
private void navTo(ToDoModel model) {
getFragmentManager().beginTransaction()
.replace(android.R.id.content, DisplayFragment.newInstance(model.id))
.addToBackStack(null)
.commit();
}
}
/*
Copyright (c) 2019 CommonsWare, LLC
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required
by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, either express or implied. See the License for the specific
language governing permissions and limitations under the License.
Covered in detail in the book _Elements of Android Jetpack_
https://commonsware.com/Jetpack
*/
package com.commonsware.jetpack.samplerj.fragments;
import java.util.List;
import androidx.lifecycle.ViewModel;
public class ListViewModel extends ViewModel {
final List<ToDoModel> items = ToDoRepository.INSTANCE.getItems();
}
/*
Copyright (c) 2019 CommonsWare, LLC
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required
by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, either express or implied. See the License for the specific
language governing permissions and limitations under the License.
Covered in detail in the book _Elements of Android Jetpack_
https://commonsware.com/Jetpack
*/
package com.commonsware.jetpack.samplerj.fragments;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(android.R.id.content, new ListFragment())
.commit();
}
}
}
/*
Copyright (c) 2019 CommonsWare, LLC
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required
by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, either express or implied. See the License for the specific
language governing permissions and limitations under the License.
Covered in detail in the book _Elements of Android Jetpack_
https://commonsware.com/Jetpack
*/
package com.commonsware.jetpack.samplerj.fragments;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import com.commonsware.jetpack.samplerj.fragments.databinding.TodoRowBinding;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ListAdapter;
class ToDoListAdapter extends ListAdapter<ToDoModel, ToDoListRowHolder> {
private final LayoutInflater inflater;
private final ToDoListRowHolder.OnRowClickListener listener;
protected ToDoListAdapter(LayoutInflater inflater,
ToDoListRowHolder.OnRowClickListener listener) {
super(ToDoModel.DIFF_CALLBACK);
this.inflater = inflater;
this.listener = listener;
}
@NonNull
@Override
public ToDoListRowHolder onCreateViewHolder(@NonNull ViewGroup parent,
int viewType) {
return new ToDoListRowHolder(
TodoRowBinding.inflate(inflater, parent, false), listener);
}
@Override
public void onBindViewHolder(@NonNull ToDoListRowHolder holder,
int position) {
holder.bind(getItem(position));
}
}
/*
Copyright (c) 2019 CommonsWare, LLC
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required
by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, either express or implied. See the License for the specific
language governing permissions and limitations under the License.
Covered in detail in the book _Elements of Android Jetpack_
https://commonsware.com/Jetpack
*/
package com.commonsware.jetpack.samplerj.fragments;
import com.commonsware.jetpack.samplerj.fragments.databinding.TodoRowBinding;
import androidx.recyclerview.widget.RecyclerView;
class ToDoListRowHolder extends RecyclerView.ViewHolder {
interface OnRowClickListener {
void onRowClick(ToDoModel model);
}
private final TodoRowBinding binding;
ToDoListRowHolder(TodoRowBinding binding, OnRowClickListener listener) {
super(binding.getRoot());
this.binding = binding;
binding.getRoot().setOnClickListener(v -> {
if (binding.getModel()!=null) {
listener.onRowClick(binding.getModel());
}
});
}
void bind(ToDoModel model) {
binding.setModel(model);
binding.executePendingBindings();
}
}
/*
Copyright (c) 2019 CommonsWare, LLC
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required
by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, either express or implied. See the License for the specific
language governing permissions and limitations under the License.
Covered in detail in the book _Elements of Android Jetpack_
https://commonsware.com/Jetpack
*/
package com.commonsware.jetpack.samplerj.fragments;
import java.util.Calendar;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.DiffUtil;
public class ToDoModel {
@NonNull
public final String id;
@NonNull
public final String description;
public final boolean isCompleted;
@Nullable
public final String notes;
@NonNull
public final Calendar createdOn;
ToDoModel(@NonNull String id, @NonNull String description,
boolean isCompleted, @Nullable String notes,
@NonNull Calendar createdOn) {
this.id = id;
this.description = description;
this.isCompleted = isCompleted;
this.notes = notes;
this.createdOn = createdOn;
}
static final DiffUtil.ItemCallback<ToDoModel> DIFF_CALLBACK =
new DiffUtil.ItemCallback<ToDoModel>() {
@Override
public boolean areItemsTheSame(@NonNull ToDoModel oldItem,
@NonNull ToDoModel newItem) {
return oldItem == newItem;
}
@Override
public boolean areContentsTheSame(@NonNull ToDoModel oldItem,
@NonNull ToDoModel newItem) {
return oldItem.isCompleted == newItem.isCompleted &&
oldItem.description.equals(newItem.description);
}
};
}
/*
Copyright (c) 2019 CommonsWare, LLC
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required
by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, either express or implied. See the License for the specific
language governing permissions and limitations under the License.
Covered in detail in the book _Elements of Android Jetpack_
https://commonsware.com/Jetpack
*/
package com.commonsware.jetpack.samplerj.fragments;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
class ToDoRepository {
static final ToDoRepository INSTANCE = new ToDoRepository();
private final Map<String, ToDoModel> items = new HashMap<>();
private ToDoRepository() {
add(new ToDoModel(UUID.randomUUID().toString(),
"Buy a copy of _Elements of Android Jetpack_", true,
"See https://wares.commonsware.com",
Calendar.getInstance()));
add(
new ToDoModel(UUID.randomUUID().toString(), "Read the entire book",
false, null,
Calendar.getInstance()));
add(new ToDoModel(UUID.randomUUID().toString(),
"Write an app for somebody in my community", false,
"Talk to some people at non-profit organizations to see what they need!",
Calendar.getInstance()));
}
@NonNull
List<ToDoModel> getItems() {
return new ArrayList<>(items.values());
}
@Nullable
ToDoModel findItemById(String id) {
return items.get(id);
}
private void add(ToDoModel model) {
items.put(model.id, model);
}
}
<vector xmlns:aapt="http://schemas.android.com/aapt"
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeColor="#00000000"
android:strokeWidth="1">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"/>
</vector>
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillColor="#008577"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"