Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 26389119 authored by calderwoodra's avatar calderwoodra Committed by android-build-merger
Browse files

Merge "Add SpeedDialEntry database helper." am: f5f774df am: 962dc917

am: ae0178c9

Change-Id: Idf23000be3bbd691c2d502e74df29ef55d3ade0a
parents 0b0fd746 ae0178c9
Loading
Loading
Loading
Loading
+116 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * 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
 */

package com.android.dialer.speeddial.database;

import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
import com.google.auto.value.AutoValue;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/** POJO representation of database rows returned by {@link SpeedDialEntryDao}. */
@AutoValue
public abstract class SpeedDialEntry {

  /** Unique ID */
  public abstract long id();

  /** @see {@link Contacts#_ID} */
  public abstract long contactId();

  /** @see {@link Contacts#LOOKUP_KEY} */
  public abstract String lookupKey();

  /**
   * {@link Channel} that is associated with this entry.
   *
   * <p>Contacts with multiple channels do not have a default until specified by the user. Once the
   * default channel is determined, all calls should be placed to this channel.
   */
  @Nullable
  public abstract Channel defaultChannel();

  public abstract Builder toBuilder();

  public static Builder builder() {
    return new AutoValue_SpeedDialEntry.Builder();
  }

  /** Builder class for speed dial entry. */
  @AutoValue.Builder
  public abstract static class Builder {

    public abstract Builder setId(long id);

    public abstract Builder setContactId(long contactId);

    public abstract Builder setLookupKey(String lookupKey);

    public abstract Builder setDefaultChannel(@Nullable Channel defaultChannel);

    public abstract SpeedDialEntry build();
  }

  /** POJO representation of a relevant phone number columns in {@link SpeedDialEntryDao}. */
  @AutoValue
  public abstract static class Channel {

    public static final int UNKNOWN = 0;
    public static final int VOICE = 1;
    public static final int VIDEO = 2;

    /** Whether the Channel is for an audio or video call. */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef({UNKNOWN, VOICE, VIDEO})
    public @interface Technology {}

    /**
     * Raw phone number as the user entered it.
     *
     * @see {@link Phone#NUMBER}
     */
    public abstract String number();

    /**
     * Label that the user associated with this number like {@link Phone#TYPE_WORK}, {@link
     * Phone#TYPE_HOME}, ect.
     *
     * @see {@link Phone#LABEL}
     */
    public abstract String label();

    public abstract @Technology int technology();

    public static Builder builder() {
      return new AutoValue_SpeedDialEntry_Channel.Builder();
    }

    /** Builder class for {@link Channel}. */
    @AutoValue.Builder
    public abstract static class Builder {

      public abstract Builder setNumber(String number);

      public abstract Builder setLabel(String label);

      public abstract Builder setTechnology(@Technology int technology);

      public abstract Channel build();
    }
  }
}
+57 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * 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
 */

package com.android.dialer.speeddial.database;

import java.util.List;

/** Interface that databases support speed dial entries should implement. */
public interface SpeedDialEntryDao {

  /** Return all entries in the database */
  List<SpeedDialEntry> getAllEntries();

  /**
   * Insert new entries.
   *
   * <p>Fails if any of the {@link SpeedDialEntry#id()} already exist.
   */
  void insert(List<SpeedDialEntry> entries);

  /**
   * Insert a new entry.
   *
   * <p>Fails if the {@link SpeedDialEntry#id()} already exists.
   */
  long insert(SpeedDialEntry entry);

  /**
   * Updates existing entries based on {@link SpeedDialEntry#id}.
   *
   * <p>Fails if the {@link SpeedDialEntry#id()} doesn't exist.
   */
  void update(List<SpeedDialEntry> entries);

  /**
   * Delete the passed in entries based on {@link SpeedDialEntry#id}.
   *
   * <p>Fails if the {@link SpeedDialEntry#id()} doesn't exist.
   */
  void delete(List<Long> entries);

  /** Delete all entries in the database. */
  void deleteAll();
}
+221 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * 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
 */

package com.android.dialer.speeddial.database;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.text.TextUtils;
import com.android.dialer.common.Assert;
import com.android.dialer.common.database.Selection;
import com.android.dialer.speeddial.database.SpeedDialEntry.Channel;
import java.util.ArrayList;
import java.util.List;

/** {@link SpeedDialEntryDao} implemented as an SQLite database. */
public final class SpeedDialEntryDatabaseHelper extends SQLiteOpenHelper
    implements SpeedDialEntryDao {

  private static final int DATABASE_VERSION = 1;
  private static final String DATABASE_NAME = "CPSpeedDialEntry";

  // Column names
  private static final String TABLE_NAME = "speed_dial_entries";
  private static final String ID = "id";
  private static final String CONTACT_ID = "contact_id";
  private static final String LOOKUP_KEY = "lookup_key";
  private static final String PHONE_NUMBER = "phone_number";
  private static final String PHONE_LABEL = "phone_label";
  private static final String PHONE_TYPE = "phone_type";

  // Column positions
  private static final int POSITION_ID = 0;
  private static final int POSITION_CONTACT_ID = 1;
  private static final int POSITION_LOOKUP_KEY = 2;
  private static final int POSITION_PHONE_NUMBER = 3;
  private static final int POSITION_PHONE_LABEL = 4;
  private static final int POSITION_PHONE_TYPE = 5;

  // Create Table Query
  private static final String CREATE_TABLE_SQL =
      "create table if not exists "
          + TABLE_NAME
          + " ("
          + (ID + " integer primary key, ")
          + (CONTACT_ID + " integer, ")
          + (LOOKUP_KEY + " text, ")
          + (PHONE_NUMBER + " text, ")
          + (PHONE_LABEL + " text, ")
          + (PHONE_TYPE + " integer ")
          + ");";

  private static final String DELETE_TABLE_SQL = "drop table if exists " + TABLE_NAME;

  public SpeedDialEntryDatabaseHelper(Context context) {
    super(context, DATABASE_NAME, null, DATABASE_VERSION);
  }

  @Override
  public void onCreate(SQLiteDatabase db) {
    db.execSQL(CREATE_TABLE_SQL);
  }

  @Override
  public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    // TODO(calderwoodra): handle upgrades more elegantly
    db.execSQL(DELETE_TABLE_SQL);
    this.onCreate(db);
  }

  @Override
  public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    // TODO(calderwoodra): handle upgrades more elegantly
    this.onUpgrade(db, oldVersion, newVersion);
  }

  @Override
  public List<SpeedDialEntry> getAllEntries() {
    List<SpeedDialEntry> entries = new ArrayList<>();

    String query = "SELECT * FROM " + TABLE_NAME;
    try (SQLiteDatabase db = getReadableDatabase();
        Cursor cursor = db.rawQuery(query, null)) {
      cursor.moveToPosition(-1);
      while (cursor.moveToNext()) {
        Channel channel =
            Channel.builder()
                .setNumber(cursor.getString(POSITION_PHONE_NUMBER))
                .setLabel(cursor.getString(POSITION_PHONE_LABEL))
                .setTechnology(cursor.getInt(POSITION_PHONE_TYPE))
                .build();
        if (TextUtils.isEmpty(channel.number())) {
          channel = null;
        }
        SpeedDialEntry entry =
            SpeedDialEntry.builder()
                .setDefaultChannel(channel)
                .setContactId(cursor.getLong(POSITION_CONTACT_ID))
                .setLookupKey(cursor.getString(POSITION_LOOKUP_KEY))
                .setId(cursor.getInt(POSITION_ID))
                .build();
        entries.add(entry);
      }
    }
    return entries;
  }

  @Override
  public void insert(List<SpeedDialEntry> entries) {
    SQLiteDatabase db = getWritableDatabase();
    db.beginTransaction();
    try {
      for (SpeedDialEntry entry : entries) {
        if (db.insert(TABLE_NAME, null, buildContentValues(entry)) == -1L) {
          throw Assert.createUnsupportedOperationFailException(
              "Attempted to insert a row that already exists.");
        }
      }
      db.setTransactionSuccessful();
    } finally {
      db.endTransaction();
      db.close();
    }
  }

  @Override
  public long insert(SpeedDialEntry entry) {
    long updateRowId;
    try (SQLiteDatabase db = getWritableDatabase()) {
      updateRowId = db.insert(TABLE_NAME, null, buildContentValues(entry));
    }
    if (updateRowId == -1) {
      throw Assert.createUnsupportedOperationFailException(
          "Attempted to insert a row that already exists.");
    }
    return updateRowId;
  }

  @Override
  public void update(List<SpeedDialEntry> entries) {
    SQLiteDatabase db = getWritableDatabase();
    db.beginTransaction();
    try {
      for (SpeedDialEntry entry : entries) {
        int count =
            db.update(
                TABLE_NAME,
                buildContentValues(entry),
                ID + " = ?",
                new String[] {Long.toString(entry.id())});
        if (count != 1) {
          throw Assert.createUnsupportedOperationFailException(
              "Attempted to update an undetermined number of rows: " + count);
        }
      }
      db.setTransactionSuccessful();
    } finally {
      db.endTransaction();
      db.close();
    }
  }

  private ContentValues buildContentValues(SpeedDialEntry entry) {
    ContentValues values = new ContentValues();
    values.put(ID, entry.id());
    values.put(CONTACT_ID, entry.contactId());
    values.put(LOOKUP_KEY, entry.lookupKey());
    if (entry.defaultChannel() != null) {
      values.put(PHONE_NUMBER, entry.defaultChannel().number());
      values.put(PHONE_LABEL, entry.defaultChannel().label());
      values.put(PHONE_TYPE, entry.defaultChannel().technology());
    }
    return values;
  }

  @Override
  public void delete(List<Long> ids) {
    List<String> idStrings = new ArrayList<>();
    for (Long id : ids) {
      idStrings.add(Long.toString(id));
    }

    Selection selection = Selection.builder().and(Selection.column(ID).in(idStrings)).build();
    try (SQLiteDatabase db = getWritableDatabase()) {
      int count = db.delete(TABLE_NAME, selection.getSelection(), selection.getSelectionArgs());
      if (count != ids.size()) {
        throw Assert.createUnsupportedOperationFailException(
            "Attempted to delete an undetermined number of rows: " + count);
      }
    }
  }

  @Override
  public void deleteAll() {
    SQLiteDatabase db = getWritableDatabase();
    db.beginTransaction();
    try {
      // Passing null into where clause will delete all rows
      db.delete(TABLE_NAME, /* whereClause=*/ null, null);
      db.setTransactionSuccessful();
    } finally {
      db.endTransaction();
      db.close();
    }
  }
}