Skip to content

Commit

Permalink
Add the ability to re-order sticker packs.
Browse files Browse the repository at this point in the history
  • Loading branch information
greyson-signal committed Jan 24, 2020
1 parent 7d70ea7 commit f7a3bb2
Show file tree
Hide file tree
Showing 13 changed files with 211 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import android.database.Cursor;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import android.text.TextUtils;
import android.util.Pair;

Expand All @@ -29,6 +30,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;

public class StickerDatabase extends Database {

Expand All @@ -43,6 +45,7 @@ public class StickerDatabase extends Database {
private static final String STICKER_ID = "sticker_id";
private static final String EMOJI = "emoji";
private static final String COVER = "cover";
private static final String PACK_ORDER = "pack_order";
private static final String INSTALLED = "installed";
private static final String LAST_USED = "last_used";
public static final String FILE_PATH = "file_path";
Expand All @@ -56,6 +59,7 @@ public class StickerDatabase extends Database {
PACK_AUTHOR + " TEXT NOT NULL, " +
STICKER_ID + " INTEGER, " +
COVER + " INTEGER, " +
PACK_ORDER + " INTEGER, " +
EMOJI + " TEXT NOT NULL, " +
LAST_USED + " INTEGER, " +
INSTALLED + " INTEGER," +
Expand Down Expand Up @@ -130,7 +134,7 @@ public void insertSticker(@NonNull IncomingSticker sticker, @NonNull InputStream
public @Nullable Cursor getInstalledStickerPacks() {
String selection = COVER + " = ? AND " + INSTALLED + " = ?";
String[] args = new String[] { "1", "1" };
Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, selection, args, null, null, null);
Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, selection, args, null, null, PACK_ORDER + " ASC");

setNotifyStickerPackListeners(cursor);
return cursor;
Expand All @@ -153,7 +157,7 @@ public void insertSticker(@NonNull IncomingSticker sticker, @NonNull InputStream
public @Nullable Cursor getAllStickerPacks(@Nullable String limit) {
String query = COVER + " = ?";
String[] args = new String[] { "1" };
Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, query, args, null, null, null, limit);
Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, query, args, null, null, PACK_ORDER + " ASC", limit);
setNotifyStickerPackListeners(cursor);

return cursor;
Expand Down Expand Up @@ -272,7 +276,6 @@ public void uninstallPack(@NonNull String packId) {

db.beginTransaction();
try {

updatePackInstalled(db, packId, false, false);
deleteStickersInPackExceptCover(db, packId);

Expand All @@ -284,6 +287,29 @@ public void uninstallPack(@NonNull String packId) {
}
}

public void updatePackOrder(@NonNull List<StickerPackRecord> packsInOrder) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();

db.beginTransaction();
try {
String selection = PACK_ID + " = ? AND " + COVER + " = ?";

for (int i = 0; i < packsInOrder.size(); i++) {
String[] args = new String[]{ packsInOrder.get(i).getPackId(), "1" };
ContentValues values = new ContentValues();

values.put(PACK_ORDER, i);

db.update(TABLE_NAME, values, selection, args);
}

db.setTransactionSuccessful();
notifyStickerPackListeners();
} finally {
db.endTransaction();
}
}

private void updatePackInstalled(@NonNull SQLiteDatabase db, @NonNull String packId, boolean installed, boolean notify) {
StickerPackRecord existing = getStickerPack(packId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
private static final int KEY_VALUE_STORE = 41;
private static final int ATTACHMENT_DISPLAY_ORDER = 42;
private static final int SPLIT_PROFILE_NAMES = 43;
private static final int STICKER_PACK_ORDER = 44;

private static final int DATABASE_VERSION = 43;
private static final int DATABASE_VERSION = 44;
private static final String DATABASE_NAME = "signal.db";

private final Context context;
Expand Down Expand Up @@ -703,6 +704,10 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("ALTER TABLE recipient ADD COLUMN profile_joined_name TEXT DEFAULT NULL");
}

if (oldVersion < STICKER_PACK_ORDER) {
db.execSQL("ALTER TABLE sticker ADD COLUMN pack_order INTEGER DEFAULT 0");
}

db.setTransactionSuccessful();
} finally {
db.endTransaction();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
import android.content.Intent;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.view.MenuItem;

import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.ShareActivity;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.util.DynamicTheme;

Expand All @@ -22,9 +24,8 @@ public final class StickerManagementActivity extends PassphraseRequiredActionBar

private final DynamicTheme dynamicTheme = new DynamicTheme();

private RecyclerView list;
private StickerManagementAdapter adapter;

private RecyclerView list;
private StickerManagementAdapter adapter;
private StickerManagementViewModel viewModel;

public static Intent getIntent(@NonNull Context context) {
Expand Down Expand Up @@ -96,6 +97,7 @@ private void initView() {

list.setLayoutManager(new LinearLayoutManager(this));
list.setAdapter(adapter);
new ItemTouchHelper(new StickerManagementItemTouchHelper(new ItemTouchCallback())).attachToRecyclerView(list);
}

private void initToolbar() {
Expand All @@ -114,4 +116,21 @@ private void initViewModel() {
adapter.setPackLists(packResult.getInstalledPacks(), packResult.getAvailablePacks(), packResult.getBlessedPacks());
});
}

private class ItemTouchCallback implements StickerManagementItemTouchHelper.Callback {
@Override
public boolean onMove(int start, int end) {
return adapter.onMove(start, end);
}

@Override
public boolean isMovable(int position) {
return adapter.isMovable(position);
}

@Override
public void onMoveCommitted() {
viewModel.onOrderChanged(adapter.getInstalledPacksInOrder());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.thoughtcrime.securesms.util.adapter.StableIdGenerator;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

final class StickerManagementAdapter extends SectionedRecyclerViewAdapter<String, StickerManagementAdapter.StickerSection> {
Expand Down Expand Up @@ -93,6 +94,30 @@ public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder) {
}
}

boolean onMove(int start, int end) {
StickerSection installed = sections.get(0);

if (!installed.isContent(start)) {
return false;
}

if (!installed.isContent(end)) {
return false;
}

installed.swap(start, end);
notifyItemMoved(start, end);
return true;
}

boolean isMovable(int position) {
return sections.get(0).isContent(position);
}

@NonNull List<StickerPackRecord> getInstalledPacksInOrder() {
return sections.get(0).records;
}

void setPackLists(@NonNull List<StickerPackRecord> installedPacks,
@NonNull List<StickerPackRecord> availablePacks,
@NonNull List<StickerPackRecord> blessedPacks)
Expand Down Expand Up @@ -185,6 +210,21 @@ void bindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder,
((StickerViewHolder) viewHolder).bind(glideRequests, eventListener, records.get(localPosition - 1), localPosition == records.size());
}
}

void swap(int start, int end) {
int localStart = getLocalPosition(start) - 1;
int localEnd = getLocalPosition(end) - 1;

if (localStart < localEnd) {
for (int i = localStart; i < localEnd; i++) {
Collections.swap(records, i, i + 1);
}
} else {
for (int i = localStart; i > localEnd; i--) {
Collections.swap(records, i, i - 1);
}
}
}
}

static class StickerViewHolder extends RecyclerView.ViewHolder {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package org.thoughtcrime.securesms.stickers;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;

public class StickerManagementItemTouchHelper extends ItemTouchHelper.Callback {

private final Callback callback;

public StickerManagementItemTouchHelper(Callback callback) {
this.callback = callback;
}

@Override
public boolean isLongPressDragEnabled() {
return true;
}

@Override
public boolean isItemViewSwipeEnabled() {
return false;
}

@Override
public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
if (callback.isMovable(viewHolder.getAdapterPosition())) {
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
return makeMovementFlags(dragFlags, 0);
} else {
return 0;
}
}

@Override
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
return callback.onMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
}

@Override
public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
callback.onMoveCommitted();
}

@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
}

public interface Callback {
/**
* @return True if both the start and end positions are valid, and therefore the move will occur.
*/
boolean onMove(int start, int end);
void onMoveCommitted();
boolean isMovable(int position);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ void installStickerPack(@NonNull String packId, @NonNull String packKey, boolean
});
}

void setPackOrder(@NonNull List<StickerPackRecord> packsInOrder) {
SignalExecutors.SERIAL.execute(() -> {
stickerDatabase.updatePackOrder(packsInOrder);
});
}

static class PackResult {

private final List<StickerPackRecord> installedPacks;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@
import androidx.annotation.NonNull;

import org.thoughtcrime.securesms.database.DatabaseContentProviders;
import org.thoughtcrime.securesms.database.model.StickerPackRecord;
import org.thoughtcrime.securesms.database.model.StickerRecord;
import org.thoughtcrime.securesms.stickers.StickerManagementRepository.PackResult;

import java.util.List;

final class StickerManagementViewModel extends ViewModel {

private final Application application;
Expand Down Expand Up @@ -56,6 +60,10 @@ void onStickerPackInstallClicked(@NonNull String packId, @NonNull String packKey
repository.installStickerPack(packId, packKey, false);
}

void onOrderChanged(List<StickerPackRecord> packsInOrder) {
repository.setPackOrder(packsInOrder);
}

@Override
protected void onCleared() {
application.getContentResolver().unregisterContentObserver(observer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ final boolean handles(int globalPosition) {
return localPosition >= 0 && localPosition < size();
}

public boolean isContent(int globalPosition) {
return handles(globalPosition) && getViewType(globalPosition) == TYPE_CONTENT;
}

public final int size() {
if (getContentSize() == 0 && hasEmptyState()) {
return 2;
Expand Down
17 changes: 17 additions & 0 deletions app/src/main/res/drawable-v21/selectable_background_dark.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple
xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/transparent_white_20">

<item android:id="@+id/mask">
<shape>
<solid android:color="@color/transparent_black" />
</shape>
</item>

<item>
<shape android:shape="rectangle" >
<solid android:color="@color/core_grey_95"/>
</shape>
</item>
</ripple>
17 changes: 17 additions & 0 deletions app/src/main/res/drawable-v21/selectable_background_light.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple
xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/transparent_black_20">

<item android:id="@+id/mask">
<shape>
<solid android:color="@color/transparent_black" />
</shape>
</item>

<item>
<shape android:shape="rectangle" >
<solid android:color="@color/core_white"/>
</shape>
</item>
</ripple>
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
android:layout_marginBottom="8dp"
android:paddingStart="@dimen/sticker_management_horizontal_margin"
android:paddingEnd="@dimen/sticker_management_horizontal_margin"
android:background="?selectableItemBackground">
android:background="?sticker_management_item_background">

<ImageView
android:id="@+id/sticker_management_cover"
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/attrs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@
<attr name="sticker_management_divider_color" format="color" />
<attr name="sticker_management_empty_background_color" format="color" />
<attr name="sticker_management_action_button_color" format="color" />
<attr name="sticker_management_item_background" format="color" />
<attr name="sticker_popup_background" format="color" />
<attr name="sticker_preview_toolbar_background" format="color" />
<attr name="sticker_preview_status_bar_color" format="color" />
Expand Down

0 comments on commit f7a3bb2

Please sign in to comment.