/* * Copyright 2015. Appsi Mobile * * 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.appsimobile.appsii.module.apps; import android.app.Fragment; import android.content.AsyncQueryHandler; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.net.Uri; import android.os.Bundle; import android.support.annotation.Nullable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import com.appsimobile.appsii.R; import com.appsimobile.appsii.module.BaseListAdapter; import com.appsimobile.appsii.module.ViewHolder; import com.mobeta.android.dslv.ConditionalRemovableAdapter; import com.mobeta.android.dslv.DragSortListView; import java.util.ArrayList; import java.util.List; /** * Created by nick on 31/08/14. */ public class ReorderAppTagsFragment extends Fragment implements DragSortListView.RemoveListener, AppTagUtils.AppTagListener, DragSortListView.DropListener { /** * The list-view used to re-order the tags. We register a few listeners on the * list to get updates when items are re-ordered or removed */ DragSortListView mDragSortListView; /** * The adapter applied to the list-view. */ TagAdapter mTagAdapter; /** * True when we are updating the database. While the database is being updated, * we ignore changes to the tags and don't update the underlying list to prevent * flickering. */ boolean mIsChangeInProgress; /** * The handler used to apply the user's changes */ @Nullable QueryHandler mQueryHandler; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // create the adapter here to make sure we can set it's data and register it's listener // This ensures the life cycles are the same mTagAdapter = new TagAdapter(); List<AppTag> appTags = AppTagUtils.getInstance(getActivity()).registerAppTagListener(this); mTagAdapter.setItems(appTags); } @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_reorder_app_tags, container, false); } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); mDragSortListView = (DragSortListView) view.findViewById(R.id.sort_list_view); mDragSortListView.setDropListener(this); mDragSortListView.setRemoveListener(this); mDragSortListView.setAdapter(mTagAdapter); } @Override public void onDestroy() { super.onDestroy(); AppTagUtils.getInstance(getActivity()).unregisterAppTagListener(this); } @Override public void remove(int i) { mIsChangeInProgress = true; AppTag item = mTagAdapter.getItem(i); mTagAdapter.removeItem(i); doRemoveTag(item); } void doRemoveTag(AppTag item) { // We only create the query-handler if it is really needed. So create // it when null if (mQueryHandler == null) { mQueryHandler = new QueryHandler(getActivity().getContentResolver()); } mQueryHandler.removeTag(item); } @Override public void onTagsChanged(ArrayList<AppTag> appTags) { if (!mIsChangeInProgress) { mTagAdapter.setItems(appTags); } } @Override public void drop(int from, int to) { if (from != to) { mIsChangeInProgress = true; doUpdateOrder(mTagAdapter.getItems(), from, to); } } /** * Starts an update in the order. The order in the database is changed to * reflect the order in the given list with the item at position from * changed to 'to'. */ private void doUpdateOrder(List<AppTag> items, int from, int to) { // We only create the query-handler if it is really needed. So create // it when null if (mQueryHandler == null) { mQueryHandler = new QueryHandler(getActivity().getContentResolver()); } // create a new list, make this list the same size as the original one, // and apply the change to it. int count = items.size(); List<AppTag> newOrdering = new ArrayList<>(count); newOrdering.addAll(items); AppTag moved = newOrdering.remove(from); newOrdering.add(to, moved); // now update the adapter to make sure it already reflects the change mTagAdapter.setItems(newOrdering); // start the update on the database mQueryHandler.updateOrdering(newOrdering); } void onDeleteFinished() { mIsChangeInProgress = false; } void onUpdateFinished() { mIsChangeInProgress = false; } public static class TagViewHolder extends ViewHolder { final TextView mTextView; public TagViewHolder(View view) { super(view); mTextView = (TextView) view.findViewById(R.id.tag_title); } } public class TagAdapter extends BaseListAdapter<AppTag, TagViewHolder> implements ConditionalRemovableAdapter { @Override protected long getItemId(AppTag appTag) { return appTag.id; } @Override protected TagViewHolder newViewHolder(LayoutInflater inflater, ViewGroup parent) { View view = inflater.inflate(R.layout.list_item_tag, parent, false); return new TagViewHolder(view); } @Override protected void bindViewHolder(AppTag item, TagViewHolder holder) { holder.mTextView.setText(item.title); } @Override public boolean hasStableIds() { return true; } @Override public boolean canRemove(int pos) { AppTag tag = getItem(pos); return tag.tagType == AppsContract.TagColumns.TAG_TYPE_USER; } } public class QueryHandler extends AsyncQueryHandler { static final int DELETE_APPS_TOKEN = 0; static final int DELETE_TAG_TOKEN = 1; private int mUpdatingCount; public QueryHandler(ContentResolver cr) { super(cr); } public void updateOrdering(List<AppTag> newOrdering) { int size = newOrdering.size(); mUpdatingCount = size; for (int i = 0; i < size; i++) { ContentValues values = new ContentValues(); values.put(AppsContract.TagColumns.POSITION, i); AppTag tag = newOrdering.get(i); Uri uri = ContentUris.withAppendedId(AppsContract.TagColumns.CONTENT_URI, tag.id); startUpdate(i, tag, uri, values, null, null); } } @Override protected void onUpdateComplete(int token, Object cookie, int result) { super.onUpdateComplete(token, cookie, result); mUpdatingCount--; if (mUpdatingCount == 0) { onUpdateFinished(); } } @Override protected void onDeleteComplete(int token, Object cookie, int result) { if (token == DELETE_APPS_TOKEN) { // The apps contained in this tag have been removed. // Now remove the tag itself. AppTag tag = (AppTag) cookie; Uri uri = ContentUris.withAppendedId(AppsContract.TagColumns.CONTENT_URI, tag.id); startDelete(1, tag, uri, null, null); } else { onDeleteFinished(); } } public void removeTag(AppTag tag) { // first remove all apps that are using this tag, // this uses token DELETE_APPS_TOKEN. Once this // is done, the tag itself must be deleted startDelete(DELETE_APPS_TOKEN, tag, AppsContract.TaggedAppColumns.CONTENT_URI, AppsContract.TaggedAppColumns.TAG_ID + "=?", new String[]{String.valueOf(tag.id)}); } } }