display spinning wheel + swipe to refresh for quicksy
This commit is contained in:
		
							parent
							
								
									fe90c6f846
								
							
						
					
					
						commit
						8a67f31368
					
				|  | @ -15,4 +15,14 @@ public class QuickConversationsService extends AbstractQuickConversationsService | |||
|     public void signalAccountStateChange() { | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean isSynchronizing() { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void considerSync(boolean force) { | ||||
| 
 | ||||
|     } | ||||
| } | ||||
|  | @ -21,4 +21,8 @@ public abstract class AbstractQuickConversationsService { | |||
|     } | ||||
| 
 | ||||
|     public abstract void signalAccountStateChange(); | ||||
| 
 | ||||
|     public abstract boolean isSynchronizing(); | ||||
| 
 | ||||
|     public abstract void considerSync(boolean force); | ||||
| } | ||||
|  |  | |||
|  | @ -66,14 +66,14 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { | |||
| 		if (avatar != null || cachedOnly) { | ||||
| 			return avatar; | ||||
| 		} | ||||
| 		if (contact.getProfilePhoto() != null) { | ||||
| 			avatar = mXmppConnectionService.getFileBackend().cropCenterSquare(Uri.parse(contact.getProfilePhoto()), size); | ||||
| 		} | ||||
| 		if (avatar == null && contact.getAvatarFilename() != null) { | ||||
| 		if (contact.getAvatarFilename() != null) { | ||||
| 			avatar = mXmppConnectionService.getFileBackend().getAvatar(contact.getAvatarFilename(), size); | ||||
| 		} | ||||
| 		if (avatar == null && contact.getProfilePhoto() != null) { | ||||
| 			avatar = mXmppConnectionService.getFileBackend().cropCenterSquare(Uri.parse(contact.getProfilePhoto()), size); | ||||
| 		} | ||||
| 		if (avatar == null) { | ||||
| 			avatar = get(contact.getDisplayName(), contact.getJid().asBareJid().toString(), size, cachedOnly); | ||||
| 			avatar = get(contact.getDisplayName(), contact.getJid().asBareJid().toString(), size, false); | ||||
| 		} | ||||
| 		this.mXmppConnectionService.getBitmapCache().put(KEY, avatar); | ||||
| 		return avatar; | ||||
|  | @ -147,10 +147,10 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { | |||
| 		if (avatar == null) { | ||||
| 			Contact contact = user.getContact(); | ||||
| 			if (contact != null) { | ||||
| 				avatar = get(contact, size, cachedOnly); | ||||
| 				avatar = get(contact, size, false); | ||||
| 			} else { | ||||
| 				String seed = user.getRealJid() != null ? user.getRealJid().asBareJid().toString() : null; | ||||
| 				avatar = get(user.getName(), seed, size, cachedOnly); | ||||
| 				avatar = get(user.getName(), seed, size, false); | ||||
| 			} | ||||
| 		} | ||||
| 		this.mXmppConnectionService.getBitmapCache().put(KEY, avatar); | ||||
|  | @ -510,11 +510,10 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { | |||
| 		Contact contact = user.getContact(); | ||||
| 		if (contact != null) { | ||||
| 			Uri uri = null; | ||||
| 			if (contact.getProfilePhoto() != null) { | ||||
| 			if (contact.getAvatarFilename() != null) { | ||||
| 				uri = mXmppConnectionService.getFileBackend().getAvatarUri(contact.getAvatarFilename()); | ||||
| 			} else if (contact.getProfilePhoto() != null) { | ||||
| 				uri = Uri.parse(contact.getProfilePhoto()); | ||||
| 			} else if (contact.getAvatarFilename() != null) { | ||||
| 				uri = mXmppConnectionService.getFileBackend().getAvatarUri( | ||||
| 						contact.getAvatarFilename()); | ||||
| 			} | ||||
| 			if (drawTile(canvas, uri, left, top, right, bottom)) { | ||||
| 				return true; | ||||
|  |  | |||
|  | @ -849,9 +849,9 @@ public class NotificationService { | |||
|         return SystemClock.elapsedRealtime() < (this.mLastNotification + miniGrace); | ||||
|     } | ||||
| 
 | ||||
|     public Notification createForegroundNotification() { | ||||
|     Notification createForegroundNotification() { | ||||
|         final Notification.Builder mBuilder = new Notification.Builder(mXmppConnectionService); | ||||
|         mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.conversations_foreground_service)); | ||||
|         mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.app_name)); | ||||
|         if (Compatibility.runsAndTargetsTwentySix(mXmppConnectionService) || Config.SHOW_CONNECTED_ACCOUNTS) { | ||||
|             List<Account> accounts = mXmppConnectionService.getAccounts(); | ||||
|             int enabled = 0; | ||||
|  |  | |||
|  | @ -22,6 +22,7 @@ import android.support.v4.app.FragmentTransaction; | |||
| import android.support.v4.app.ListFragment; | ||||
| import android.support.v4.view.PagerAdapter; | ||||
| import android.support.v4.view.ViewPager; | ||||
| import android.support.v4.widget.SwipeRefreshLayout; | ||||
| import android.support.v7.app.ActionBar; | ||||
| import android.support.v7.app.AlertDialog; | ||||
| import android.support.v7.widget.Toolbar; | ||||
|  | @ -72,13 +73,14 @@ import eu.siacs.conversations.ui.util.JidDialog; | |||
| import eu.siacs.conversations.ui.util.MenuDoubleTabUtil; | ||||
| import eu.siacs.conversations.ui.util.PendingItem; | ||||
| import eu.siacs.conversations.ui.util.SoftKeyboardUtils; | ||||
| import eu.siacs.conversations.ui.widget.SwipeRefreshListFragment; | ||||
| import eu.siacs.conversations.utils.AccountUtils; | ||||
| import eu.siacs.conversations.utils.XmppUri; | ||||
| import eu.siacs.conversations.xmpp.OnUpdateBlocklist; | ||||
| import eu.siacs.conversations.xmpp.XmppConnection; | ||||
| import rocks.xmpp.addr.Jid; | ||||
| 
 | ||||
| public class StartConversationActivity extends XmppActivity implements XmppConnectionService.OnConversationUpdate, OnRosterUpdate, OnUpdateBlocklist, CreateConferenceDialog.CreateConferenceDialogListener, JoinConferenceDialog.JoinConferenceDialogListener { | ||||
| public class StartConversationActivity extends XmppActivity implements XmppConnectionService.OnConversationUpdate, OnRosterUpdate, OnUpdateBlocklist, CreateConferenceDialog.CreateConferenceDialogListener, JoinConferenceDialog.JoinConferenceDialogListener, SwipeRefreshLayout.OnRefreshListener { | ||||
| 
 | ||||
| 	public static final String EXTRA_INVITE_URI = "eu.siacs.conversations.invite_uri"; | ||||
| 
 | ||||
|  | @ -757,6 +759,9 @@ public class StartConversationActivity extends XmppActivity implements XmppConne | |||
| 			Log.d(Config.LOGTAG, "calling on backend connected on dialog"); | ||||
| 			((OnBackendConnected) fragment).onBackendConnected(); | ||||
| 		} | ||||
| 		if (QuickConversationsService.isQuicksy()) { | ||||
| 			setRefreshing(xmppConnectionService.getQuickConversationsService().isSynchronizing()); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	protected boolean processViewIntent(@NonNull Intent intent) { | ||||
|  | @ -877,16 +882,6 @@ public class StartConversationActivity extends XmppActivity implements XmppConne | |||
| 		mContactsAdapter.notifyDataSetChanged(); | ||||
| 	} | ||||
| 
 | ||||
| 	private static boolean isSingleAccountActive(final List<Account> accounts) { | ||||
| 		int i = 0; | ||||
| 		for(Account account : accounts) { | ||||
| 			if (account.getStatus() != Account.State.DISABLED) { | ||||
| 				++i; | ||||
| 			} | ||||
| 		} | ||||
| 		return i == 1; | ||||
| 	} | ||||
| 
 | ||||
| 	protected void filterConferences(String needle) { | ||||
| 		this.conferences.clear(); | ||||
| 		for (Account account : xmppConnectionService.getAccounts()) { | ||||
|  | @ -924,6 +919,9 @@ public class StartConversationActivity extends XmppActivity implements XmppConne | |||
| 			filter(mSearchEditText.getText().toString()); | ||||
| 		} | ||||
| 		configureHomeButton(); | ||||
| 		if (QuickConversationsService.isQuicksy()) { | ||||
| 			setRefreshing(xmppConnectionService.getQuickConversationsService().isSynchronizing()); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
|  | @ -1006,7 +1004,23 @@ public class StartConversationActivity extends XmppActivity implements XmppConne | |||
| 		refreshUi(); | ||||
| 	} | ||||
| 
 | ||||
| 	public static class MyListFragment extends ListFragment { | ||||
| 	@Override | ||||
| 	public void onRefresh() { | ||||
| 		Log.d(Config.LOGTAG,"user requested to refresh"); | ||||
| 		if (QuickConversationsService.isQuicksy() && xmppConnectionService != null) { | ||||
| 			xmppConnectionService.getQuickConversationsService().considerSync(true); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 
 | ||||
| 	private void setRefreshing(boolean refreshing) { | ||||
| 		MyListFragment fragment = (MyListFragment) mListPagerAdapter.getItem(0); | ||||
| 		if (fragment != null) { | ||||
| 			fragment.setRefreshing(refreshing); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	public static class MyListFragment extends SwipeRefreshListFragment { | ||||
| 		private AdapterView.OnItemClickListener mOnItemClickListener; | ||||
| 		private int mResContextMenu; | ||||
| 
 | ||||
|  | @ -1164,10 +1178,12 @@ public class StartConversationActivity extends XmppActivity implements XmppConne | |||
| 					listFragment.setContextMenu(R.menu.conference_context); | ||||
| 					listFragment.setOnListItemClickListener((arg0, arg1, p, arg3) -> openConversationForBookmark(p)); | ||||
| 				} else { | ||||
| 
 | ||||
| 					listFragment.setListAdapter(mContactsAdapter); | ||||
| 					listFragment.setContextMenu(R.menu.contact_context); | ||||
| 					listFragment.setOnListItemClickListener((arg0, arg1, p, arg3) -> openConversationForContact(p)); | ||||
| 					if (QuickConversationsService.isQuicksy()) { | ||||
| 						listFragment.setOnRefreshListener(StartConversationActivity.this); | ||||
| 					} | ||||
| 				} | ||||
| 				fragments[position] = listFragment; | ||||
| 			} | ||||
|  |  | |||
|  | @ -0,0 +1,148 @@ | |||
| /* | ||||
|  * Copyright 2014 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 eu.siacs.conversations.ui.widget; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.os.Bundle; | ||||
| import android.support.v4.app.ListFragment; | ||||
| import android.support.v4.view.ViewCompat; | ||||
| import android.support.v4.widget.SwipeRefreshLayout; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.ListView; | ||||
| 
 | ||||
| import eu.siacs.conversations.R; | ||||
| import eu.siacs.conversations.ui.util.StyledAttributes; | ||||
| 
 | ||||
| /** | ||||
|  * Subclass of {@link android.support.v4.app.ListFragment} which provides automatic support for | ||||
|  * providing the 'swipe-to-refresh' UX gesture by wrapping the the content view in a | ||||
|  * {@link android.support.v4.widget.SwipeRefreshLayout}. | ||||
|  */ | ||||
| public class SwipeRefreshListFragment extends ListFragment { | ||||
| 
 | ||||
|     private boolean enabled = false; | ||||
|     private boolean refreshing = false; | ||||
| 
 | ||||
|     private SwipeRefreshLayout.OnRefreshListener onRefreshListener; | ||||
| 
 | ||||
|     private SwipeRefreshLayout mSwipeRefreshLayout; | ||||
| 
 | ||||
|     @Override | ||||
|     public View onCreateView(LayoutInflater inflater, ViewGroup container, | ||||
|                              Bundle savedInstanceState) { | ||||
| 
 | ||||
|         // Create the list fragment's content view by calling the super method | ||||
|         final View listFragmentView = super.onCreateView(inflater, container, savedInstanceState); | ||||
| 
 | ||||
|         // Now create a SwipeRefreshLayout to wrap the fragment's content view | ||||
|         mSwipeRefreshLayout = new ListFragmentSwipeRefreshLayout(container.getContext()); | ||||
|         mSwipeRefreshLayout.setEnabled(enabled); | ||||
|         mSwipeRefreshLayout.setRefreshing(refreshing); | ||||
| 
 | ||||
|         final Context context = getActivity(); | ||||
|         if (context != null) { | ||||
|             mSwipeRefreshLayout.setColorSchemeColors(StyledAttributes.getColor(context, R.attr.colorAccent)); | ||||
|         } | ||||
| 
 | ||||
|         if (onRefreshListener != null) { | ||||
|             mSwipeRefreshLayout.setOnRefreshListener(onRefreshListener); | ||||
|         } | ||||
| 
 | ||||
|         // Add the list fragment's content view to the SwipeRefreshLayout, making sure that it fills | ||||
|         // the SwipeRefreshLayout | ||||
|         mSwipeRefreshLayout.addView(listFragmentView, | ||||
|                 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); | ||||
| 
 | ||||
|         // Make sure that the SwipeRefreshLayout will fill the fragment | ||||
|         mSwipeRefreshLayout.setLayoutParams( | ||||
|                 new ViewGroup.LayoutParams( | ||||
|                         ViewGroup.LayoutParams.MATCH_PARENT, | ||||
|                         ViewGroup.LayoutParams.MATCH_PARENT)); | ||||
| 
 | ||||
|         // Now return the SwipeRefreshLayout as this fragment's content view | ||||
|         return mSwipeRefreshLayout; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Set the {@link android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener} to listen for | ||||
|      * initiated refreshes. | ||||
|      * | ||||
|      * @see android.support.v4.widget.SwipeRefreshLayout#setOnRefreshListener(android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener) | ||||
|      */ | ||||
|     public void setOnRefreshListener(SwipeRefreshLayout.OnRefreshListener listener) { | ||||
|         onRefreshListener = listener; | ||||
|         enabled = true; | ||||
|         if (mSwipeRefreshLayout != null) { | ||||
|             mSwipeRefreshLayout.setEnabled(true); | ||||
|             mSwipeRefreshLayout.setOnRefreshListener(listener); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Set whether the {@link android.support.v4.widget.SwipeRefreshLayout} should be displaying | ||||
|      * that it is refreshing or not. | ||||
|      * | ||||
|      * @see android.support.v4.widget.SwipeRefreshLayout#setRefreshing(boolean) | ||||
|      */ | ||||
|     public void setRefreshing(boolean refreshing) { | ||||
|         this.refreshing = refreshing; | ||||
|         if (mSwipeRefreshLayout != null) { | ||||
|             mSwipeRefreshLayout.setRefreshing(refreshing); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Sub-class of {@link android.support.v4.widget.SwipeRefreshLayout} for use in this | ||||
|      * {@link android.support.v4.app.ListFragment}. The reason that this is needed is because | ||||
|      * {@link android.support.v4.widget.SwipeRefreshLayout} only supports a single child, which it | ||||
|      * expects to be the one which triggers refreshes. In our case the layout's child is the content | ||||
|      * view returned from | ||||
|      * {@link android.support.v4.app.ListFragment#onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle)} | ||||
|      * which is a {@link android.view.ViewGroup}. | ||||
|      * | ||||
|      * <p>To enable 'swipe-to-refresh' support via the {@link android.widget.ListView} we need to | ||||
|      * override the default behavior and properly signal when a gesture is possible. This is done by | ||||
|      * overriding {@link #canChildScrollUp()}. | ||||
|      */ | ||||
|     private class ListFragmentSwipeRefreshLayout extends SwipeRefreshLayout { | ||||
| 
 | ||||
|         public ListFragmentSwipeRefreshLayout(Context context) { | ||||
|             super(context); | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * As mentioned above, we need to override this method to properly signal when a | ||||
|          * 'swipe-to-refresh' is possible. | ||||
|          * | ||||
|          * @return true if the {@link android.widget.ListView} is visible and can scroll up. | ||||
|          */ | ||||
|         @Override | ||||
|         public boolean canChildScrollUp() { | ||||
|             final ListView listView = getListView(); | ||||
|             if (listView.getVisibility() == View.VISIBLE) { | ||||
|                 return listView.canScrollVertically(-1); | ||||
|             } else { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -27,6 +27,7 @@ import java.util.WeakHashMap; | |||
| import java.util.concurrent.CountDownLatch; | ||||
| import java.util.concurrent.TimeUnit; | ||||
| import java.util.concurrent.atomic.AtomicBoolean; | ||||
| import java.util.concurrent.atomic.AtomicInteger; | ||||
| 
 | ||||
| import javax.net.ssl.SSLHandshakeException; | ||||
| 
 | ||||
|  | @ -68,6 +69,7 @@ public class QuickConversationsService extends AbstractQuickConversationsService | |||
| 
 | ||||
|     private final AtomicBoolean mVerificationInProgress = new AtomicBoolean(false); | ||||
|     private final AtomicBoolean mVerificationRequestInProgress = new AtomicBoolean(false); | ||||
|     private final AtomicInteger mRunningSyncJobs = new AtomicInteger(0); | ||||
|     private CountDownLatch awaitingAccountStateChange; | ||||
| 
 | ||||
|     private Attempt mLastSyncAttempt = Attempt.NULL; | ||||
|  | @ -282,12 +284,23 @@ public class QuickConversationsService extends AbstractQuickConversationsService | |||
|         return mVerificationRequestInProgress.get(); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean isSynchronizing() { | ||||
|         return mRunningSyncJobs.get() > 0; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void considerSync() { | ||||
|         considerSync(false); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void considerSync(boolean forced) { | ||||
|         Map<String, PhoneNumberContact> contacts = PhoneNumberContact.load(service); | ||||
|         for (Account account : service.getAccounts()) { | ||||
|             refresh(account, contacts.values()); | ||||
|             if (!considerSync(account, contacts)) { | ||||
|             if (!considerSync(account, contacts, forced)) { | ||||
|                 service.syncRoster(account); | ||||
|             } | ||||
|         } | ||||
|  | @ -313,13 +326,14 @@ public class QuickConversationsService extends AbstractQuickConversationsService | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private boolean considerSync(Account account, final Map<String, PhoneNumberContact> contacts) { | ||||
|     private boolean considerSync(Account account, final Map<String, PhoneNumberContact> contacts, final boolean forced) { | ||||
|         final int hash = contacts.keySet().hashCode(); | ||||
|         Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": consider sync of " + hash); | ||||
|         if (!mLastSyncAttempt.retry(hash)) { | ||||
|         if (!mLastSyncAttempt.retry(hash) && !forced) { | ||||
|             Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": do not attempt sync"); | ||||
|             return false; | ||||
|         } | ||||
|         mRunningSyncJobs.incrementAndGet(); | ||||
|         final Jid syncServer = Jid.of(API_DOMAIN); | ||||
|         Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": sending phone list to " + syncServer); | ||||
|         List<Element> entries = new ArrayList<>(); | ||||
|  | @ -366,6 +380,7 @@ public class QuickConversationsService extends AbstractQuickConversationsService | |||
|             } else { | ||||
|                 Log.d(Config.LOGTAG,account.getJid().asBareJid()+": failed to sync contact list with api server"); | ||||
|             } | ||||
|             mRunningSyncJobs.decrementAndGet(); | ||||
|             service.syncRoster(account); | ||||
|             service.updateRosterUi(); | ||||
|         }); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Daniel Gultsch
						Daniel Gultsch