Added favorite local contacts

pull/153/head
M66B 6 years ago
parent cbf1a41c1f
commit f420104775

File diff suppressed because it is too large Load Diff

@ -21,7 +21,9 @@ package eu.faircode.email;
import android.Manifest; import android.Manifest;
import android.content.Context; import android.content.Context;
import android.content.res.ColorStateList;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle;
import android.text.format.DateUtils; import android.text.format.DateUtils;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@ -34,21 +36,25 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.lifecycle.LifecycleOwner;
import androidx.recyclerview.widget.DiffUtil; import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListUpdateCallback; import androidx.recyclerview.widget.ListUpdateCallback;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
public class AdapterContact extends RecyclerView.Adapter<AdapterContact.ViewHolder> { public class AdapterContact extends RecyclerView.Adapter<AdapterContact.ViewHolder> {
private Context context; private Context context;
private LifecycleOwner owner;
private LayoutInflater inflater; private LayoutInflater inflater;
private boolean contacts; private boolean contacts;
private int colorAccent;
private int textColorSecondary;
private List<EntityContact> all = new ArrayList<>(); private List<EntityContact> all = new ArrayList<>();
private List<EntityContact> filtered = new ArrayList<>(); private List<EntityContact> filtered = new ArrayList<>();
private static NumberFormat nf = NumberFormat.getNumberInstance(); private static NumberFormat nf = NumberFormat.getNumberInstance();
public class ViewHolder extends RecyclerView.ViewHolder { public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private View itemView; private View itemView;
private ImageView ivType; private ImageView ivType;
private ImageView ivAvatar; private ImageView ivAvatar;
@ -56,6 +62,7 @@ public class AdapterContact extends RecyclerView.Adapter<AdapterContact.ViewHold
private TextView tvEmail; private TextView tvEmail;
private TextView tvTimes; private TextView tvTimes;
private TextView tvLast; private TextView tvLast;
private ImageView ivFavorite;
ViewHolder(View itemView) { ViewHolder(View itemView) {
super(itemView); super(itemView);
@ -67,6 +74,15 @@ public class AdapterContact extends RecyclerView.Adapter<AdapterContact.ViewHold
tvEmail = itemView.findViewById(R.id.tvEmail); tvEmail = itemView.findViewById(R.id.tvEmail);
tvTimes = itemView.findViewById(R.id.tvTimes); tvTimes = itemView.findViewById(R.id.tvTimes);
tvLast = itemView.findViewById(R.id.tvLast); tvLast = itemView.findViewById(R.id.tvLast);
ivFavorite = itemView.findViewById(R.id.ivFavorite);
}
private void wire() {
itemView.setOnClickListener(this);
}
private void unwire() {
itemView.setOnClickListener(null);
} }
private void bindTo(EntityContact contact) { private void bindTo(EntityContact contact) {
@ -87,13 +103,55 @@ public class AdapterContact extends RecyclerView.Adapter<AdapterContact.ViewHold
tvTimes.setText(nf.format(contact.times_contacted)); tvTimes.setText(nf.format(contact.times_contacted));
tvLast.setText(contact.last_contacted == null ? null tvLast.setText(contact.last_contacted == null ? null
: DateUtils.getRelativeTimeSpanString(context, contact.last_contacted)); : DateUtils.getRelativeTimeSpanString(context, contact.last_contacted));
ivFavorite.setImageResource(contact.favorite ? R.drawable.baseline_star_24 : R.drawable.baseline_star_border_24);
ivFavorite.setImageTintList(ColorStateList.valueOf(contact.favorite ? colorAccent : textColorSecondary));
}
@Override
public void onClick(View view) {
int pos = getAdapterPosition();
if (pos == RecyclerView.NO_POSITION)
return;
EntityContact contact = filtered.get(pos);
Bundle args = new Bundle();
args.putLong("id", contact.id);
args.putBoolean("favorite", !contact.favorite);
new SimpleTask<Void>() {
@Override
protected Void onExecute(Context context, Bundle args) {
long id = args.getLong("id");
boolean favorite = args.getBoolean("favorite");
DB db = DB.getInstance(context);
db.contact().setContactFavorite(id, favorite);
return null;
}
@Override
protected void onExecuted(Bundle args, Void data) {
Shortcuts.update(context, owner);
}
@Override
protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(context, owner, ex);
}
}.execute(context, owner, args, "contact:favorite");
} }
} }
AdapterContact(Context context) { AdapterContact(Context context, LifecycleOwner owner) {
this.context = context; this.context = context;
this.owner = owner;
this.inflater = LayoutInflater.from(context); this.inflater = LayoutInflater.from(context);
this.contacts = Helper.hasPermission(context, Manifest.permission.READ_CONTACTS); this.contacts = Helper.hasPermission(context, Manifest.permission.READ_CONTACTS);
this.colorAccent = Helper.resolveColor(context, R.attr.colorAccent);
this.textColorSecondary = Helper.resolveColor(context, android.R.attr.textColorSecondary);
setHasStableIds(true); setHasStableIds(true);
} }
@ -183,7 +241,9 @@ public class AdapterContact extends RecyclerView.Adapter<AdapterContact.ViewHold
@Override @Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) { public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.unwire();
EntityContact contact = filtered.get(position); EntityContact contact = filtered.get(position);
holder.bindTo(contact); holder.bindTo(contact);
holder.wire();
} }
} }

@ -50,7 +50,7 @@ import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory;
// https://developer.android.com/topic/libraries/architecture/room.html // https://developer.android.com/topic/libraries/architecture/room.html
@Database( @Database(
version = 55, version = 56,
entities = { entities = {
EntityIdentity.class, EntityIdentity.class,
EntityAccount.class, EntityAccount.class,
@ -608,6 +608,13 @@ public abstract class DB extends RoomDatabase {
db.execSQL("ALTER TABLE `contact` ADD COLUMN `last_contacted` INTEGER"); db.execSQL("ALTER TABLE `contact` ADD COLUMN `last_contacted` INTEGER");
} }
}) })
.addMigrations(new Migration(55, 56) {
@Override
public void migrate(SupportSQLiteDatabase db) {
Log.i("DB migration from version " + startVersion + " to " + endVersion);
db.execSQL("ALTER TABLE `contact` ADD COLUMN `favorite` INTEGER NOT NULL DEFAULT 0");
}
})
.build(); .build();
} }

@ -35,11 +35,11 @@ public interface DaoContact {
List<EntityContact> getContacts(); List<EntityContact> getContacts();
@Query("SELECT * FROM contact" + @Query("SELECT * FROM contact" +
" ORDER BY times_contacted DESC, last_contacted DESC") " ORDER BY favorite DESC, times_contacted DESC, last_contacted DESC")
LiveData<List<EntityContact>> liveContacts(); LiveData<List<EntityContact>> liveContacts();
@Query("SELECT * FROM contact" + @Query("SELECT * FROM contact" +
" ORDER BY times_contacted DESC, last_contacted DESC" + " ORDER BY favorite DESC, times_contacted DESC, last_contacted DESC" +
" LIMIT :count") " LIMIT :count")
List<EntityContact> getFrequentlyContacted(int count); List<EntityContact> getFrequentlyContacted(int count);
@ -66,6 +66,9 @@ public interface DaoContact {
@Update @Update
int updateContact(EntityContact contact); int updateContact(EntityContact contact);
@Query("UPDATE contact SET favorite = :favorite WHERE id = :id")
int setContactFavorite(long id, boolean favorite);
@Query("DELETE from contact") @Query("DELETE from contact")
int clearContacts(); int clearContacts();
} }

@ -60,6 +60,8 @@ public class EntityContact implements Serializable {
@NonNull @NonNull
public Integer times_contacted; public Integer times_contacted;
public Long last_contacted; public Long last_contacted;
@NonNull
public Boolean favorite = false;
public JSONObject toJSON() throws JSONException { public JSONObject toJSON() throws JSONException {
JSONObject json = new JSONObject(); JSONObject json = new JSONObject();
@ -70,6 +72,7 @@ public class EntityContact implements Serializable {
json.put("avatar", avatar); json.put("avatar", avatar);
json.put("times_contacted", times_contacted); json.put("times_contacted", times_contacted);
json.put("last_contacted", last_contacted); json.put("last_contacted", last_contacted);
json.put("favorite", favorite);
return json; return json;
} }
@ -93,6 +96,9 @@ public class EntityContact implements Serializable {
if (json.has("last_contacted") && !json.isNull("last_contacted")) if (json.has("last_contacted") && !json.isNull("last_contacted"))
contact.last_contacted = json.getLong("last_contacted"); contact.last_contacted = json.getLong("last_contacted");
if (json.has("favorite"))
contact.favorite = json.getBoolean("favorite");
return contact; return contact;
} }
@ -105,7 +111,8 @@ public class EntityContact implements Serializable {
Objects.equals(this.name, other.name) && Objects.equals(this.name, other.name) &&
Objects.equals(this.avatar, other.avatar) && Objects.equals(this.avatar, other.avatar) &&
this.times_contacted == other.times_contacted && this.times_contacted == other.times_contacted &&
Objects.equals(this.last_contacted, other.last_contacted)); Objects.equals(this.last_contacted, other.last_contacted) &&
this.favorite == other.favorite);
} else } else
return false; return false;

@ -66,7 +66,7 @@ public class FragmentContacts extends FragmentBase {
LinearLayoutManager llm = new LinearLayoutManager(getContext()); LinearLayoutManager llm = new LinearLayoutManager(getContext());
rvContacts.setLayoutManager(llm); rvContacts.setLayoutManager(llm);
adapter = new AdapterContact(getContext()); adapter = new AdapterContact(getContext(), getViewLifecycleOwner());
rvContacts.setAdapter(adapter); rvContacts.setAdapter(adapter);
// Initialize // Initialize

@ -59,9 +59,10 @@
android:id="@+id/tvTimes" android:id="@+id/tvTimes"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="6dp"
android:text="123" android:text="123"
android:textAppearance="@android:style/TextAppearance.Small" android:textAppearance="@android:style/TextAppearance.Small"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toStartOf="@+id/ivFavorite"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<TextView <TextView
@ -69,8 +70,20 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="3dp" android:layout_marginTop="3dp"
android:layout_marginEnd="6dp"
android:text="12:34:56" android:text="12:34:56"
android:textAppearance="@android:style/TextAppearance.Small" android:textAppearance="@android:style/TextAppearance.Small"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toStartOf="@+id/ivFavorite"
app:layout_constraintTop_toBottomOf="@+id/tvTimes" /> app:layout_constraintTop_toBottomOf="@+id/tvTimes" />
<ImageView
android:id="@+id/ivFavorite"
android:layout_width="24dp"
android:layout_height="24dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:src="@drawable/baseline_star_24"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="@+id/ivAvatar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/ivAvatar" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
Loading…
Cancel
Save