diff --git a/app/build.gradle b/app/build.gradle index 4f5b8271e8cf52d8c02c376ae4c25ac68094c018..c6d07b1c3628c07c65c8fdf0934b2c72c8f49046 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,4 +1,4 @@ -apply plugin: 'com.android.application' +apply plugin: 'android' apply plugin: 'newlens' android { @@ -13,13 +13,15 @@ android { versionName "2.4" } + /* signingConfigs { release { } } + buildTypes { debug { - signingConfig signingConfigs.release + storeFile file("debug.keystore") } release { signingConfig signingConfigs.release @@ -31,6 +33,7 @@ android { lintOptions { abortOnError false } + */ } dependencies { @@ -44,9 +47,11 @@ dependencies { compile 'com.networkbench.newlens.agent.android:nbs.newlens.agent:2.2.5' compile 'com.google.zxing:core:3.2.0' compile 'com.joanzapata.android:android-iconify:1.0.9' + compile 'com.belerweb:pinyin4j:2.5.0' } // 配置签名文件以及相关的账号信息 +/* File propFile = new File('sign.properties') if (propFile.exists()) { def Properties props = new Properties() @@ -64,3 +69,4 @@ if (propFile.exists()) { } else { android.buildTypes.release.signingConfig = null } +*/ \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 72cd19687ef19ff23acd295323e61fdb5750bc91..7fba0ecf54d992eebe6b4ee7ce41a29a0301c6ac 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ + package="net.oschina.app" > @@ -121,21 +121,24 @@ - + - - + + - + android:screenOrientation="portrait" + android:windowSoftInputMode="stateHidden|adjustResize" /> - - - + android:screenOrientation="portrait" /> - + + @@ -191,12 +195,12 @@ - + - + - + android:screenOrientation="portrait" + android:theme="@style/Theme.Transparent" /> + + diff --git a/app/src/main/java/net/oschina/app/AppException.java b/app/src/main/java/net/oschina/app/AppException.java index 5f76356f2ac00cae8a5ea9d7779c2fbd13a23c6e..75eeda4a9af3ada31a9eef3c7004c9b3adc11b06 100644 --- a/app/src/main/java/net/oschina/app/AppException.java +++ b/app/src/main/java/net/oschina/app/AppException.java @@ -154,6 +154,7 @@ public class AppException extends Exception implements UncaughtExceptionHandler } boolean success = true; try { + ex.printStackTrace(); success = saveToSDCard(ex); } catch (Exception e) { } finally { diff --git a/app/src/main/java/net/oschina/app/api/ApiHttpClient.java b/app/src/main/java/net/oschina/app/api/ApiHttpClient.java index 59613bf921f94f47a8d5f45bcfebb3042234355c..1059570600022a860a6d09de72966ef57aa5f082 100644 --- a/app/src/main/java/net/oschina/app/api/ApiHttpClient.java +++ b/app/src/main/java/net/oschina/app/api/ApiHttpClient.java @@ -18,8 +18,8 @@ public class ApiHttpClient { // public final static String HOST = "www.oschina.net"; // private static String API_URL = "http://www.oschina.net/%s"; - public final static String HOST = "192.168.1.107"; - private static String API_URL = "http://192.168.1.107/%s"; + public final static String HOST = "www.oschina.net"; + private static String API_URL = "http://www.oschina.net/%s"; public static final String DELETE = "DELETE"; public static final String GET = "GET"; public static final String POST = "POST"; diff --git a/app/src/main/java/net/oschina/app/api/remote/OSChinaApi.java b/app/src/main/java/net/oschina/app/api/remote/OSChinaApi.java index a671887902f4deccb74db2bb2a7596a5af72bcf7..6d7f153d26a57587c4c0edbb47cf28712053b2ab 100644 --- a/app/src/main/java/net/oschina/app/api/remote/OSChinaApi.java +++ b/app/src/main/java/net/oschina/app/api/remote/OSChinaApi.java @@ -1007,4 +1007,16 @@ public class OSChinaApi { ApiHttpClient.post("action/api/openid_reg", params, handler); } + /** + * 获取全部好友 + * @param uid 用户id + * @param handler + */ + public static void getAllFriendList(int uid,AsyncHttpResponseHandler handler) { + RequestParams params = new RequestParams(); + params.put("uid", uid); + params.put("all", 1); + params.put("relation", 1); + ApiHttpClient.get("action/api/friends_list", params, handler); + } } diff --git a/app/src/main/java/net/oschina/app/fragment/TweetPubFragment.java b/app/src/main/java/net/oschina/app/fragment/TweetPubFragment.java index 27558fa962309fc9eead182388be9e80cf06afb7..8cc9b83811c2dec439a03b677aedf94684a074fa 100644 --- a/app/src/main/java/net/oschina/app/fragment/TweetPubFragment.java +++ b/app/src/main/java/net/oschina/app/fragment/TweetPubFragment.java @@ -36,6 +36,7 @@ import net.oschina.app.emoji.Emojicon; import net.oschina.app.emoji.InputHelper; import net.oschina.app.emoji.OnEmojiClickListener; import net.oschina.app.service.ServerTaskUtils; +import net.oschina.app.ui.SelectUserActivity; import net.oschina.app.util.DialogHelp; import net.oschina.app.util.FileUtil; import net.oschina.app.util.ImageUtils; @@ -64,6 +65,8 @@ import butterknife.InjectView; public class TweetPubFragment extends BaseFragment implements OnEmojiClickListener { + private static final int REQUEST_CODE_AT_USER = 0x1225; + public static final int ACTION_TYPE_ALBUM = 0; public static final int ACTION_TYPE_PHOTO = 1; public static final int ACTION_TYPE_RECORD = 2; // 录音 @@ -412,6 +415,14 @@ public class TweetPubFragment extends BaseFragment implements final Intent imageReturnIntent) { if (resultCode != Activity.RESULT_OK) return; + + // at user return + if(requestCode == REQUEST_CODE_AT_USER){ + String atUsersText = imageReturnIntent.getStringExtra(SelectUserActivity.RESULT_AT_USERS); + if(!TextUtils.isEmpty(atUsersText)) + mEtInput.getText().replace(mEtInput.getSelectionStart(),mEtInput.getSelectionEnd(),atUsersText); + } + new Thread() { private String selectedImagePath; @@ -595,30 +606,32 @@ public class TweetPubFragment extends BaseFragment implements } private void insertMention() { - TDevice.showSoftKeyboard(mEtInput); + // TDevice.showSoftKeyboard(mEtInput); // 在光标所在处插入“@用户名” - int curTextLength = mEtInput.getText().length(); - if (curTextLength >= MAX_TEXT_LENGTH) - return; - String atme = TEXT_ATME; - int start, end; - if ((MAX_TEXT_LENGTH - curTextLength) >= atme.length()) { - start = mEtInput.getSelectionStart() + 1; - end = start + atme.length() - 2; - } else { - int num = MAX_TEXT_LENGTH - curTextLength; - if (num < atme.length()) { - atme = atme.substring(0, num); - } - start = mEtInput.getSelectionStart() + 1; - end = start + atme.length() - 1; - } - if (start > MAX_TEXT_LENGTH || end > MAX_TEXT_LENGTH) { - start = MAX_TEXT_LENGTH; - end = MAX_TEXT_LENGTH; - } - mEtInput.getText().insert(mEtInput.getSelectionStart(), atme); - mEtInput.setSelection(start, end);// 设置选中文字 +// int curTextLength = mEtInput.getText().length(); +// if (curTextLength >= MAX_TEXT_LENGTH) +// return; +// String atme = TEXT_ATME; +// int start, end; +// if ((MAX_TEXT_LENGTH - curTextLength) >= atme.length()) { +// start = mEtInput.getSelectionStart() + 1; +// end = start + atme.length() - 2 +// } else { +// int num = MAX_TEXT_LENGTH - curTextLength; +// if (num < atme.length()) { +// atme = atme.substring(0, num); +// } +// start = mEtInput.getSelectionStart() + 1; +// end = start + atme.length() - 1; +// } +// if (start > MAX_TEXT_LENGTH || end > MAX_TEXT_LENGTH) { +// start = MAX_TEXT_LENGTH; +// end = MAX_TEXT_LENGTH; +// } +// mEtInput.getText().insert(mEtInput.getSelectionStart(), atme); +// mEtInput.setSelection(start, end);// 设置选中文字 + Intent selectUserIntent = new Intent(getActivity(), SelectUserActivity.class); + startActivityForResult(selectUserIntent,REQUEST_CODE_AT_USER); } private void insertTrendSoftware() { diff --git a/app/src/main/java/net/oschina/app/ui/SelectUserActivity.java b/app/src/main/java/net/oschina/app/ui/SelectUserActivity.java new file mode 100644 index 0000000000000000000000000000000000000000..acdd6b7a6e44463b6db50fc6ea07292a7b49e424 --- /dev/null +++ b/app/src/main/java/net/oschina/app/ui/SelectUserActivity.java @@ -0,0 +1,697 @@ +package net.oschina.app.ui; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.inputmethodservice.Keyboard; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.support.v7.app.ActionBar; + +import android.os.Bundle; +import android.text.Editable; +import android.text.Html; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.util.SparseArray; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.InputMethodManager; +import android.widget.AbsListView; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.EditText; +import android.widget.HorizontalScrollView; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.RelativeLayout; +import android.widget.TextView; +import android.widget.Toast; + +import com.loopj.android.http.AsyncHttpResponseHandler; +import net.oschina.app.AppContext; +import net.oschina.app.R; +import net.oschina.app.api.remote.OSChinaApi; +import net.oschina.app.base.BaseActivity; +import net.oschina.app.bean.Friend; +import net.oschina.app.bean.FriendsList; +import net.oschina.app.ui.empty.EmptyLayout; +import net.oschina.app.util.StringUtils; +import net.oschina.app.util.XmlUtils; +import net.oschina.app.widget.AlphabetBar; +import net.oschina.app.widget.ImageGrid; +import net.oschina.app.widget.InputConnectEditText; + +import org.apache.http.Header; +import org.kymjs.kjframe.KJBitmap; + +import java.io.ByteArrayInputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + + +/** + * + * @author Darcy www.darcye.com + * + */ +public class SelectUserActivity extends BaseActivity { + + private static final String HIGHLIGHT_COLOR = "#cc0000"; + private static final String HIGHLIGHT_REPLACEMENT = String.format("$1",HIGHLIGHT_COLOR); + private static final char OTHER_INDEX_CHAR = Character.MAX_VALUE; + + private static final int MSG_DATA_PROCESS = 0x10000; + private static final int MSG_DATA_PROCESS_SUCCESS = 0x10001; + + private static final int MSG_SEARCH_FRIENDS = 0x20000; + private static final int MSG_SEARCH_FRIENDS_SUCCESS = 0x20001; + + private static final int MSG_REFLESH_SEARCH_BAR = 0x30000; + + private static final int MAX_SELECT_USER_COUNT = 10; + + public static final String RESULT_AT_USERS = "at_users"; + + private EmptyLayout mEmptyLayout; + private TextView mTvConfirm; + private RelativeLayout mLayoutSearchBar; + private InputConnectEditText mEtSearch; + private HorizontalScrollView mHsvSelectedUsers; + private ImageGrid mIgSelectedUsers; + private AlphabetBar mAlphaBar; + private TextView mTvAlphaTip; + private ListView mLvFriends; + private FriendListAdapter mLvAdapter; + + private List mAllMdlFriends; + private List mShowMdlFriends; + private Set mSelectedFriends = new LinkedHashSet<>(); + private Map mFriendIndexs; + + private HandlerThread mBackgroundThread; + private BackgroundHandler mBackgroundHandler; + + private KJBitmap mkjBitmap; + private String mSearchText; + private int mSelectUserPicSize; + private int mTotalFriendSize; + + private boolean mEtSearchWidthChanged; + private int mMinEtSearchWidth; + + private Handler mUIHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + if(msg.what == MSG_DATA_PROCESS_SUCCESS){ + mLvAdapter = new FriendListAdapter(); + mLvFriends.setAdapter(mLvAdapter); + initAlphaBar(); + mEmptyLayout.setVisibility(View.GONE); + }else if(msg.what == MSG_SEARCH_FRIENDS_SUCCESS){ + mLvAdapter.notifyDataSetChanged(); + }else if(msg.what == MSG_REFLESH_SEARCH_BAR){ + final int etWidth = mEtSearch.getWidth(); + final int searchBarWidth = mLayoutSearchBar.getWidth(); + final int selectUsersWidth = mIgSelectedUsers.getWidth(); + if(etWidth < mMinEtSearchWidth && !mEtSearchWidthChanged){ + ViewGroup.LayoutParams shorterLp = mHsvSelectedUsers.getLayoutParams(); + shorterLp.width = searchBarWidth - etWidth; + mHsvSelectedUsers.setLayoutParams(shorterLp); + mEtSearchWidthChanged = true; + }else if(mEtSearchWidthChanged && (searchBarWidth - selectUsersWidth) > mMinEtSearchWidth){ + ViewGroup.LayoutParams normalLp = mHsvSelectedUsers.getLayoutParams(); + normalLp.width = ViewGroup.LayoutParams.WRAP_CONTENT; + mHsvSelectedUsers.setLayoutParams(normalLp); + mEtSearchWidthChanged = false; + } + + } + } + }; + + private AsyncHttpResponseHandler mFriendsListHandler =new AsyncHttpResponseHandler() { + @Override + public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) { + FriendsList friendList = XmlUtils.toBean(FriendsList.class, responseBody); + if(friendList == null || friendList.getFriendlist().isEmpty()){ + mEmptyLayout.setErrorType(EmptyLayout.NODATA); + }else{ + mBackgroundHandler.sendMessage(mBackgroundHandler.obtainMessage(MSG_DATA_PROCESS,friendList)); + } + } + + @Override + public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) { + mEmptyLayout.setErrorType(EmptyLayout.NETWORK_ERROR); + } + }; + + private TextWatcher mSearchTextWatcher = new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + + } + + @Override + public void afterTextChanged(Editable s) { + final String searchText = s.toString(); + mSearchText = searchText; + mBackgroundHandler.removeMessages(MSG_SEARCH_FRIENDS); + mBackgroundHandler.sendMessageDelayed(mBackgroundHandler.obtainMessage(MSG_SEARCH_FRIENDS, searchText),50); + } + }; + + private final Comparator friendSearchComparator = new Comparator() { + @Override + public int compare(ItemModel lhs, ItemModel rhs) { + final String lhsUserName = lhs.primaryPinyin; + final String rhsUserName = rhs.primaryPinyin; + final String lowerCaseKeyword = mSearchText.toLowerCase(); + int result = lhsUserName.toLowerCase().indexOf(lowerCaseKeyword) - rhsUserName.toLowerCase().indexOf(lowerCaseKeyword); + if(result == 0){ + return lhsUserName.compareTo(rhsUserName); + }else{ + return result; + } + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setActionBarTitle("选择@好友"); + requestFriendsList(); + mBackgroundThread = new HandlerThread("Work Thread On SelectUserActivity"); + mBackgroundThread.start(); + mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper()); + mkjBitmap = new KJBitmap(); + mSelectUserPicSize = getResources().getDimensionPixelSize(R.dimen.select_user_pic_size); + } + + @Override + protected boolean hasBackButton() { + return true; + } + + @Override + protected void initActionBar(ActionBar actionBar) { + super.initActionBar(actionBar); + actionBar.setDisplayOptions(actionBar.getDisplayOptions() | ActionBar.DISPLAY_SHOW_CUSTOM); + mTvConfirm = new TextView(actionBar.getThemedContext()); + ActionBar.LayoutParams params = new ActionBar.LayoutParams( ActionBar.LayoutParams.WRAP_CONTENT, ActionBar.LayoutParams.WRAP_CONTENT, + Gravity.END | Gravity.CENTER_VERTICAL); + mTvConfirm.setLayoutParams(params); + mTvConfirm.setText("确定"); + mTvConfirm.setTextColor(getResources().getColor(R.color.white)); + mTvConfirm.setOnClickListener(this); + actionBar.setCustomView(mTvConfirm); + } + + @Override + protected int getLayoutId() { + return R.layout.activity_select_user; + } + + @Override + public void initData() { + mMinEtSearchWidth = getResources().getDimensionPixelSize(R.dimen.min_et_search_friend_width); + } + + @Override + public void initView() { + mEmptyLayout = (EmptyLayout)findViewById(R.id.error_layout); + mLvFriends = (ListView)findViewById(R.id.search_result_listview); + mLayoutSearchBar = (RelativeLayout)findViewById(R.id.search_bar); + mEtSearch = (InputConnectEditText)findViewById(R.id.search_edit_text); + mEtSearch.addTextChangedListener(mSearchTextWatcher); + mAlphaBar = (AlphabetBar)findViewById(R.id.alphabet_bar); + mTvAlphaTip = (TextView)findViewById(R.id.alphabet_tip); + mIgSelectedUsers = (ImageGrid)findViewById(R.id.selected_users_ig); + mHsvSelectedUsers = (HorizontalScrollView)findViewById(R.id.selected_users_sv); + mLvFriends.setOnScrollListener(new AbsListView.OnScrollListener() { + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + if(scrollState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL){ + hideInputMethod(); + } + } + + @Override + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + } + }); + mLvFriends.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + ItemModel m = mShowMdlFriends.get(position); + if (m.type == ItemModel.TYPE_USER_INFO) { + Friend f = (Friend) m.data; + if (mSelectedFriends.contains(f)) { + mSelectedFriends.remove(f); + } else if (!checkIfExceedMaxSelectCount()) { + mSelectedFriends.add(f); + } + mLvAdapter.refleshItemStatus(position, f); + refleshSelectUsers(); + } + } + }); + + mIgSelectedUsers.setOnImageGridItemClickListener(new ImageGrid.OnImageGridItemClickListener() { + @Override + public void onImageClick(int position) { + removeFriend(position); + } + }); + + mEmptyLayout.setOnLayoutClickListener(new View.OnClickListener() { + + @Override + public void onClick(View v) { + mEmptyLayout.setErrorType(EmptyLayout.NETWORK_LOADING); + requestFriendsList(); + } + }); + + mEtSearch.setOnKeyListener(new View.OnKeyListener() { + @Override + public boolean onKey(View v, int keyCode, KeyEvent event) { + + return false; + } + }); + + mEtSearch.setOnSoftKeyListener(new InputConnectEditText.OnSoftKeyListener() { + @Override + public boolean onKey(KeyEvent event) { + if (event.getAction() == KeyEvent.ACTION_UP && event.getKeyCode() == KeyEvent.KEYCODE_DEL && mEtSearch.getText().length() == 0) { + int selectSize = mSelectedFriends.size(); + if (selectSize != 0) { + removeFriend(selectSize - 1); + return true; + } + } + return false; + } + }); + } + + private void removeFriend(int position){ + Friend removeFriend = null; + final int removeIndex = position; + int i = 0; + for (Friend f : mSelectedFriends) { + if (i == removeIndex) { + removeFriend = f; + break; + } + ++i; + } + if (removeFriend != null) { + mSelectedFriends.remove(removeFriend); + mLvAdapter.notifyDataSetChanged(); + refleshSelectUsers(); + } + } + + private void refleshSelectUsers(){ + int selectFriendsSize = mSelectedFriends.size(); + mTvConfirm.setText(String.format("确定(%s/%s)", selectFriendsSize, MAX_SELECT_USER_COUNT)); + if(selectFriendsSize == 0){ + mIgSelectedUsers.setVisibility(View.GONE); + }else { + mIgSelectedUsers.setVisibility(View.VISIBLE); + Bitmap[] userBitmaps = new Bitmap[mSelectedFriends.size()]; + int i = 0; + for(Friend f : mSelectedFriends){ + userBitmaps[i++] = getCacheUserBitmap(f.getPortrait()); + } + mIgSelectedUsers.setBitmaps(userBitmaps); + } + mUIHandler.sendEmptyMessageDelayed(MSG_REFLESH_SEARCH_BAR,200); + } + + private boolean checkIfExceedMaxSelectCount(){ + boolean isExceed = mSelectedFriends.size() >= MAX_SELECT_USER_COUNT; + if(isExceed){ + Toast.makeText(this,String.format("选择用户数不能超过%s个",MAX_SELECT_USER_COUNT),Toast.LENGTH_SHORT).show(); + } + return isExceed; + } + + private Bitmap getCacheUserBitmap(String url){ + if(TextUtils.isEmpty(url)) + return BitmapFactory.decodeResource(getResources(), R.drawable.widget_dface); + + Bitmap bm = mkjBitmap.getMemoryCache(url); + if(bm == null){ + byte[] bmBytes = mkjBitmap.getCache(url); + if(bmBytes != null){ + bm = BitmapFactory.decodeStream(new ByteArrayInputStream(bmBytes)); + }else{ + bm = BitmapFactory.decodeResource(getResources(), R.drawable.widget_dface); + } + } + + return bm; + } + + @Override + public void onClick(View v) { + StringBuilder atUsers = new StringBuilder(); + for(Friend f : mSelectedFriends){ + atUsers.append(String.format("@%s ", f.getName())); + } + Intent resultIntent = new Intent(); + resultIntent.putExtra(RESULT_AT_USERS,atUsers.toString()); + setResult(RESULT_OK, resultIntent); + finish(); + } + + private void initAlphaBar(){ + mAlphaBar.setVisibility(View.VISIBLE); + mAlphaBar.setOnAlphabetTouchListener(new AlphabetBar.OnAlphabetTouchListener() { + @Override + public void onTouchMove(char al) { + FriendIndex friendIndex; + if (al == AlphabetBar.OTHER_CHARACTOR) { + friendIndex = mFriendIndexs.get(OTHER_INDEX_CHAR); + } else { + friendIndex = mFriendIndexs.get(al); + } + mTvAlphaTip.setVisibility(View.VISIBLE); + mTvAlphaTip.setText(String.valueOf(al)); + if (friendIndex != null) { + mLvFriends.setSelection(friendIndex.position); + } + } + + @Override + public void onTouchCancel() { + mTvAlphaTip.setVisibility(View.GONE); + } + }); + } + + private void requestFriendsList(){ + AppContext appContext = (AppContext) getApplication(); + OSChinaApi.getAllFriendList(appContext.getLoginUid(), mFriendsListHandler); + } + + private void clearHighLightTag(){ + for(ItemModel model : mShowMdlFriends){ + if(model.data instanceof Friend){ + Friend user = (Friend) model.data; + user.setName(user.getName().replaceAll("<.+?>", "")); + } + } + } + + private void hideInputMethod() { + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(mEtSearch.getWindowToken(), 0); + } + + private class ItemModel{ + static final int TYPE_CHARACTER = 0x0; + static final int TYPE_USER_INFO = 0x1; + int type; + Object data; + String primaryPinyin; + } + + private class FriendListAdapter extends BaseAdapter{ + + Activity activity; + + SparseArray selectorHolder; + + FriendListAdapter(){ + this.activity = SelectUserActivity.this; + selectorHolder = new SparseArray<>(); + } + + @Override + public int getCount() { + return mShowMdlFriends == null ? 0 : mShowMdlFriends.size(); + } + + @Override + public Object getItem(int position) { + return mShowMdlFriends.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public int getItemViewType(int position) { + return mShowMdlFriends.get(position).type; + } + + @Override + public int getViewTypeCount() { + return 2; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + final int itemType = getItemViewType(position); + final ItemModel model = mShowMdlFriends.get(position); + if(itemType == ItemModel.TYPE_CHARACTER){ + return getAlphaTitleView(model,convertView); + }else{ + return getItemView(position,model,convertView); + } + } + + private View getAlphaTitleView(final ItemModel model,View convertView){ + TextView tvletter; + if(convertView == null){ + tvletter = (TextView) View.inflate(activity, R.layout.item_letter_index_title, null); + }else{ + tvletter = (TextView)convertView; + } + Character letter = (Character)model.data; + if(letter == OTHER_INDEX_CHAR){ + tvletter.setText("#"); + }else{ + tvletter.setText(String.valueOf(letter)); + } + return tvletter; + } + + private View getItemView(final int position, final ItemModel model,View convertView){ + ItemViewHolder viewHodler; + if(convertView == null){ + convertView = View.inflate(activity,R.layout.list_select_friend,null); + viewHodler = new ItemViewHolder(); + viewHodler.ivUserAvator = (ImageView)convertView.findViewById(R.id.iv_user_avator); + viewHodler.tvUserName = (TextView)convertView.findViewById(R.id.tv_user_name); + viewHodler.ivSelector = (ImageView)convertView.findViewById(R.id.iv_selector); + convertView.setTag(viewHodler); + }else{ + viewHodler = (ItemViewHolder)convertView.getTag(); + } + + Friend friend = (Friend)model.data; + viewHodler.tvUserName.setText(Html.fromHtml(friend.getName())); + + selectorHolder.put(position,viewHodler.ivSelector); + + + if(mSelectedFriends.contains(friend)){ + viewHodler.ivSelector.setImageResource(R.drawable.bg_cb_friend_selected); + }else{ + viewHodler.ivSelector.setImageResource(R.drawable.bg_cb_friend_unselect); + } + + mkjBitmap.display(viewHodler.ivUserAvator,friend.getPortrait()); + + return convertView; + } + + void refleshItemStatus(int position, Friend friend){ + ImageView ivSelector = selectorHolder.get(position); + if(ivSelector != null){ + if(mSelectedFriends.contains(friend)){ + ivSelector.setImageResource(R.drawable.bg_cb_friend_selected); + }else{ + ivSelector.setImageResource(R.drawable.bg_cb_friend_unselect); + } + } + } + + private class ItemViewHolder{ + ImageView ivUserAvator; + TextView tvUserName; + ImageView ivSelector; + } + } + + public class BackgroundHandler extends Handler { + + public BackgroundHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + if(msg.what == MSG_DATA_PROCESS){ + FriendsList friendList = (FriendsList)msg.obj; + processData(friendList); + mUIHandler.sendEmptyMessage(MSG_DATA_PROCESS_SUCCESS); + }else if(msg.what == MSG_SEARCH_FRIENDS){ + String searchText = (String)msg.obj; + if(mShowMdlFriends != null){ + clearHighLightTag(); + mShowMdlFriends.clear(); + } + + if(!TextUtils.isEmpty(searchText)){ + searchFriends(searchText); + }else{ + mShowMdlFriends.addAll(mAllMdlFriends); + } + + mUIHandler.sendEmptyMessage(MSG_SEARCH_FRIENDS_SUCCESS); + } + } + + private void processData(FriendsList friendList){ + if(friendList != null) { + + List allFriends = friendList.getFriendlist(); + if (allFriends == null || allFriends.isEmpty()) { + // no friends + return; + } + + mTotalFriendSize = allFriends.size(); + mFriendIndexs = new TreeMap<>(); + Character firstAlpha; + + for (Friend f : allFriends) { + firstAlpha = StringUtils.getFirstLetter(f.getName().charAt(0)); + if ('A' <= firstAlpha && firstAlpha <= 'Z') { + addUserIndex(firstAlpha, f); + } else { + addUserIndex(OTHER_INDEX_CHAR, f); + } + } + + mShowMdlFriends = new ArrayList<>(); + + ItemModel model; + FriendIndex friendIndex; + int itemPos = 0; + for (java.util.Map.Entry entry : mFriendIndexs.entrySet()) { + model = new ItemModel(); + model.type = ItemModel.TYPE_CHARACTER; + model.data = entry.getKey(); + mShowMdlFriends.add(model); + friendIndex = entry.getValue(); + friendIndex.position = itemPos; + ++itemPos; + for (Friend friend : friendIndex.friends) { + model = new ItemModel(); + model.type = ItemModel.TYPE_USER_INFO; + model.data = friend; + model.primaryPinyin = StringUtils.toPrimaryPinyin(friend.getName()); + mShowMdlFriends.add(model); + ++itemPos; + } + } + + mAllMdlFriends = new ArrayList<>(); + mAllMdlFriends.addAll(mShowMdlFriends); + } + } + + private void addUserIndex(char letter, Friend friend){ + FriendIndex friendIndex = mFriendIndexs.get(letter); + if(friendIndex == null){ + friendIndex = new FriendIndex(); + mFriendIndexs.put(letter, friendIndex); + } + friendIndex.friends.add(friend); + } + + private void searchFriends(String searchText){ + if(mAllMdlFriends == null || mAllMdlFriends.isEmpty()) + return; + + for(ItemModel m : mAllMdlFriends){ + if(m.type == ItemModel.TYPE_USER_INFO && m.data instanceof Friend){ + Friend f = (Friend) m.data; + String name = f.getName(); + String lowerSearchText = searchText.toLowerCase(); + + if(name.toLowerCase().contains(lowerSearchText)){ + f.setName(name.replaceAll("((?i)" + searchText + ")",HIGHLIGHT_REPLACEMENT)); + mShowMdlFriends.add(m); + continue; + } + + String lowerCasePrimaryPinyin = m.primaryPinyin.toLowerCase(); + if(lowerCasePrimaryPinyin.contains(lowerSearchText)){ + String highlightName = highlightName(f.getName(),lowerSearchText, lowerCasePrimaryPinyin); + f.setName(highlightName); + mShowMdlFriends.add(m); + } + } + } + + Collections.sort(mShowMdlFriends, friendSearchComparator); + } + + private String highlightName(String name , String keyWord , String primaryPinyin){ + if(name == null || primaryPinyin == null || name.length() != primaryPinyin.length()) + return name; + + int startIndex, endIndex; + + StringBuilder srcBuilder = new StringBuilder(name); + StringBuilder primaryPinyinBuidler = new StringBuilder(primaryPinyin); + + startIndex = primaryPinyin.indexOf(keyWord); + String beginTag = String.format("", HIGHLIGHT_COLOR); + String endTag = ""; + while(startIndex != -1){ + srcBuilder.insert(startIndex, beginTag); + primaryPinyinBuidler.insert(startIndex, beginTag); + endIndex = startIndex + keyWord.length() + beginTag.length(); + srcBuilder.insert(endIndex, endTag); + primaryPinyinBuidler.insert(endIndex, endTag); + startIndex = primaryPinyinBuidler.indexOf(keyWord, endIndex + endTag.length()); + } + + return srcBuilder.toString(); + } + } + + private class FriendIndex{ + int position; + List friends = new ArrayList<>(); + } +} diff --git a/app/src/main/java/net/oschina/app/util/StringUtils.java b/app/src/main/java/net/oschina/app/util/StringUtils.java index 7a56fcbc4360b23e3205d2f7bbaf255e20e2ded8..7d6b9fa8e1ab14ab388c36f4de41021707f79ea3 100644 --- a/app/src/main/java/net/oschina/app/util/StringUtils.java +++ b/app/src/main/java/net/oschina/app/util/StringUtils.java @@ -1,5 +1,9 @@ package net.oschina.app.util; +import net.sourceforge.pinyin4j.PinyinHelper; +import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat; +import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -476,4 +480,43 @@ public class StringUtils { return df.format(new Date()); } + private static final HanyuPinyinOutputFormat PINGYIN_FORMAT = new HanyuPinyinOutputFormat(); + /** + * 获取一个汉字的拼音首字母。 (大写) + * + * @return 如果是汉字则返回首字母,否则直接返回 + */ + public static Character getFirstLetter(char ch) { + + if (('A' <= ch && ch <= 'Z') || ('a' <= ch && ch <= 'z')) { + return Character.toUpperCase(ch); + } + + try { + String[] pinyins = PinyinHelper.toHanyuPinyinStringArray(ch, PINGYIN_FORMAT); + if(pinyins == null || pinyins.length == 0){ + return ch; + }else{ + return Character.toUpperCase(pinyins[0].charAt(0)); + } + } catch (BadHanyuPinyinOutputFormatCombination e) { + e.printStackTrace(); + } + + return ch; + } + + /** + * 返回首字母组成的字符串,英文(统一大写)和特殊字符不变化 + * @param str + * @return + */ + public static String toPrimaryPinyin(String str){ + char[] strChars = str.toCharArray(); + StringBuilder primaryPinyin = new StringBuilder(); + for(char ch : strChars){ + primaryPinyin.append(getFirstLetter(ch)); + } + return primaryPinyin.toString(); + } } diff --git a/app/src/main/java/net/oschina/app/widget/AlphabetBar.java b/app/src/main/java/net/oschina/app/widget/AlphabetBar.java new file mode 100644 index 0000000000000000000000000000000000000000..f8dbc16f6e820bc63ba4f69a101e844a42e95a93 --- /dev/null +++ b/app/src/main/java/net/oschina/app/widget/AlphabetBar.java @@ -0,0 +1,137 @@ +package net.oschina.app.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Paint.FontMetricsInt; +import android.text.TextPaint; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; + +import net.oschina.app.R; + +/** + * + * @author Darcy www.darcye.com + * + */ +public class AlphabetBar extends View { + + public static final char OTHER_CHARACTOR = '#'; + + private static final char[] ALPHABETS = new char[27]; + + private TextPaint mPaint; + + private int mUnitHeight; + private int letterYOffset; + private boolean mHasInitParams = false; + + private OnAlphabetTouchListener mOnAlphabetTouchListener; + + private OnSizeChangedListener mOnSizeChangedListener; + + static{ + for(char ch = 'A'; ch <= 'Z' ; ++ch){ + ALPHABETS[ch - 'A'] = ch; + } + ALPHABETS[26] = OTHER_CHARACTOR; + } + + public AlphabetBar(Context context){ + this(context,null); + } + + public AlphabetBar(Context context, AttributeSet attrs) { + super(context, attrs); + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AlphabetBar); + final int textSize = a.getDimensionPixelSize(R.styleable.AlphabetBar_alphaTextSize, 12); + final int textColor = a.getColor(R.styleable.AlphabetBar_alphaTextColor, 0xFF000000); + mPaint = new TextPaint(); + mPaint.setAntiAlias(true); + mPaint.setTextSize(textSize); + mPaint.setColor(textColor); + a.recycle(); + setClickable(true); + } + + public void setOnAlphabetTouchListener(OnAlphabetTouchListener l) { + this.mOnAlphabetTouchListener = l; + } + + public void setOnSizeChangedListener( + OnSizeChangedListener l) { + this.mOnSizeChangedListener = l; + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + final int width = getWidth(); + if(!mHasInitParams){ + final int paddingTop = getPaddingTop(); + final int paddingBottom = getPaddingBottom(); + final int height = getHeight() - paddingTop - paddingBottom; + this.mUnitHeight = height / ALPHABETS.length; + final FontMetricsInt fontMetrics = mPaint.getFontMetricsInt(); + this.letterYOffset = (int) ((mUnitHeight - (fontMetrics.bottom - fontMetrics.top)) * 0.5 - fontMetrics.top + paddingTop); + mHasInitParams = false; + } + + int letterXOffset; + String letter; + for(int i = 0 ; i < ALPHABETS.length ; ++i){ + letter = String.valueOf(ALPHABETS[i]); + letterXOffset = (int) ((width - mPaint.measureText(letter)) * 0.5); + canvas.drawText(letter, letterXOffset, letterYOffset + mUnitHeight * i, mPaint); + } + } + + @Override + public boolean dispatchTouchEvent(MotionEvent event) { + super.dispatchTouchEvent(event); + return true; + } + + private int originIndex = -1; + @Override + public boolean onTouchEvent(MotionEvent event) { + super.onTouchEvent(event); + final int action = event.getAction(); + final int paddingTop = getPaddingTop(); + if(action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE){ + final float eventY = event.getY(); + final int newIndex = (int) ((eventY - paddingTop) / mUnitHeight) ; + if(originIndex != newIndex && 0 <= newIndex && newIndex < ALPHABETS.length){ + originIndex = newIndex; + if(mOnAlphabetTouchListener != null){ + mOnAlphabetTouchListener.onTouchMove(ALPHABETS[newIndex]); + } + } + }else if(action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL){ + originIndex = -1; + if(mOnAlphabetTouchListener != null) + mOnAlphabetTouchListener.onTouchCancel(); + } + return true; + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + if(mOnSizeChangedListener != null) + mOnSizeChangedListener.onSizeChanged(w, h, oldw, oldh); + } + + public interface OnAlphabetTouchListener{ + void onTouchMove(char al); + + void onTouchCancel(); + } + + public interface OnSizeChangedListener{ + void onSizeChanged(int w, int h, int oldw, int oldh); + } +} diff --git a/app/src/main/java/net/oschina/app/widget/ImageGrid.java b/app/src/main/java/net/oschina/app/widget/ImageGrid.java new file mode 100644 index 0000000000000000000000000000000000000000..b41400c3b9b096f0ec8beb0430504fa0f27018d6 --- /dev/null +++ b/app/src/main/java/net/oschina/app/widget/ImageGrid.java @@ -0,0 +1,150 @@ +package net.oschina.app.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.util.SparseArray; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; + +import net.oschina.app.R; + +/** + * + * 图片表格控件,它会根据占用的width和规定的列数来平均每张图片的大小。 + * + * @author Darcy www.darcye.com + * + */ +public class ImageGrid extends View { + + private int mImageNumber; + + private Bitmap[] mBitmaps; + + private SparseArray mPositionRectMap; + private ViewConfiguration mViewConfiguration; + + private OnImageGridItemClickListener mOnImageGridItemClickListener; + + private int mImageSize; + private int mImageGap; + + public ImageGrid(Context context, AttributeSet attrs) { + super(context, attrs); + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ImageGrid); + mImageGap = a.getDimensionPixelSize(R.styleable.ImageGrid_ImageGap, 5); + mImageSize = a.getDimensionPixelSize(R.styleable.ImageGrid_ImageSize, 15); + a.recycle(); + mViewConfiguration = ViewConfiguration.get(context); + } + + public void setOnImageGridItemClickListener( + OnImageGridItemClickListener onImageGridItemClickListener) { + mOnImageGridItemClickListener = onImageGridItemClickListener; + } + + /** + * 设置 + * @param bitmaps + */ + public void setBitmaps(Bitmap[] bitmaps){ + this.mBitmaps = bitmaps; + this.mImageNumber = bitmaps.length; + this.mPositionRectMap = new SparseArray<>(bitmaps.length); + invalidate(); + requestLayout(); + } + + @Override + protected void onDraw(Canvas canvas) { + + if(mImageNumber <= 0){ + return; + } + + Rect rect; + int curLeft = 0 ; + for(int imagePos = 0; imagePos < mImageNumber ; ++imagePos ){ + rect = mPositionRectMap.get(imagePos); + if(rect == null){ + rect = new Rect(); + rect.left = curLeft; + rect.top = 0; + rect.right = curLeft + mImageSize; + rect.bottom = mImageSize; + mPositionRectMap.put(imagePos, rect); + } + canvas.drawBitmap(mBitmaps[imagePos], null, rect, null); + curLeft += mImageSize + mImageGap; + } + } + + + private float touchDownX, touchDownY; + private boolean isValidClick; + @Override + public boolean dispatchTouchEvent(MotionEvent event) { + switch(event.getAction()){ + case MotionEvent.ACTION_DOWN: + touchDownX = event.getX(); + touchDownY = event.getY(); + isValidClick = true; + break; + case MotionEvent.ACTION_MOVE: + if(!isValidClick && (Math.abs(touchDownY - event.getY()) > mViewConfiguration.getScaledTouchSlop() + || Math.abs(touchDownX - event.getX()) > mViewConfiguration.getScaledTouchSlop())){ + isValidClick = false; + } + break; + case MotionEvent.ACTION_CANCEL: + isValidClick = false; + break; + case MotionEvent.ACTION_UP: + if(isValidClick){ + final int width = getWidth(); + final int validItemWidth = width / mImageNumber; + int clickPos = (int) Math.ceil(touchDownX / validItemWidth); + if(mOnImageGridItemClickListener != null && clickPos >= 1){ + mOnImageGridItemClickListener.onImageClick(clickPos-1); + } + } + isValidClick = false; + break; + } + return true; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + + if(mImageNumber <= 0){ + setMeasuredDimension(0, 0); + return; + } + + final int specWidthSize; + if(mImageNumber == 1){ + specWidthSize = mImageSize; + }else{ + specWidthSize = (mImageSize * mImageNumber) + mImageGap * (mImageNumber - 1); + } + + int specHeightSize = mImageSize; + + setMeasuredDimension(specWidthSize, specHeightSize); + } + + + public interface OnImageGridItemClickListener{ + /** + * call back when clicking image + * + */ + void onImageClick(int position); + } +} diff --git a/app/src/main/java/net/oschina/app/widget/InputConnectEditText.java b/app/src/main/java/net/oschina/app/widget/InputConnectEditText.java new file mode 100644 index 0000000000000000000000000000000000000000..1a5fe0bd105ec6716b66f91cd5888dd74faf883b --- /dev/null +++ b/app/src/main/java/net/oschina/app/widget/InputConnectEditText.java @@ -0,0 +1,52 @@ +package net.oschina.app.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; +import android.view.inputmethod.InputConnectionWrapper; +import android.widget.EditText; + +/** + * Created by Darcy on 2015/8/28. + */ +public class InputConnectEditText extends EditText{ + + private OnSoftKeyListener mOnSoftKeyListener; + + public InputConnectEditText(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public void setOnSoftKeyListener(OnSoftKeyListener l){ + this.mOnSoftKeyListener = l; + } + + @Override + public InputConnection onCreateInputConnection(EditorInfo outAttrs) { + return new ZanyInputConnection(super.onCreateInputConnection(outAttrs), + true); + } + + private class ZanyInputConnection extends InputConnectionWrapper { + + public ZanyInputConnection(InputConnection target, boolean mutable) { + super(target, mutable); + } + + @Override + public boolean sendKeyEvent(KeyEvent event) { + if(mOnSoftKeyListener != null){ + if(mOnSoftKeyListener.onKey(event)){ + return true; + } + } + return super.sendKeyEvent(event); + } + } + + public interface OnSoftKeyListener{ + boolean onKey(KeyEvent event); + } +} diff --git a/app/src/main/res/drawable-xhdpi/bg_edit_gray.9.png b/app/src/main/res/drawable-xhdpi/bg_edit_gray.9.png new file mode 100644 index 0000000000000000000000000000000000000000..79388d5b401b5cb7c0e1319ace20538e47dd1859 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/bg_edit_gray.9.png differ diff --git a/app/src/main/res/drawable-xhdpi/cb_circle_normal.png b/app/src/main/res/drawable-xhdpi/cb_circle_normal.png new file mode 100644 index 0000000000000000000000000000000000000000..cf886cc9e121187e14d0d1423d9bc89b793b840f Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/cb_circle_normal.png differ diff --git a/app/src/main/res/drawable-xhdpi/cb_circle_selected.png b/app/src/main/res/drawable-xhdpi/cb_circle_selected.png new file mode 100644 index 0000000000000000000000000000000000000000..d8d1732b0b33fbed6cf46dce6935225d099a29eb Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/cb_circle_selected.png differ diff --git a/app/src/main/res/drawable/bg_alphabet_bar.xml b/app/src/main/res/drawable/bg_alphabet_bar.xml new file mode 100644 index 0000000000000000000000000000000000000000..802b26641ee955704d903c8bf7e1b0e2c2fd2101 --- /dev/null +++ b/app/src/main/res/drawable/bg_alphabet_bar.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_alphabet_tip.xml b/app/src/main/res/drawable/bg_alphabet_tip.xml new file mode 100644 index 0000000000000000000000000000000000000000..aa3929422bf95a79e19bf0e9f2a4a2254d189097 --- /dev/null +++ b/app/src/main/res/drawable/bg_alphabet_tip.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_cb_friend_selected.xml b/app/src/main/res/drawable/bg_cb_friend_selected.xml new file mode 100644 index 0000000000000000000000000000000000000000..06aeabf1523a72cc684f2b39459cd8906c20939d --- /dev/null +++ b/app/src/main/res/drawable/bg_cb_friend_selected.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_cb_friend_unselect.xml b/app/src/main/res/drawable/bg_cb_friend_unselect.xml new file mode 100644 index 0000000000000000000000000000000000000000..c4b427a00415bd48374e1900acfda6655ef23cbd --- /dev/null +++ b/app/src/main/res/drawable/bg_cb_friend_unselect.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_select_user.xml b/app/src/main/res/layout/activity_select_user.xml new file mode 100644 index 0000000000000000000000000000000000000000..cbb6fcc490645bb707fb74bcafe9dc8fcf4aa7af --- /dev/null +++ b/app/src/main/res/layout/activity_select_user.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_letter_index_title.xml b/app/src/main/res/layout/item_letter_index_title.xml new file mode 100644 index 0000000000000000000000000000000000000000..17dba942efca84dc25dc9c77654896ee9c518f8d --- /dev/null +++ b/app/src/main/res/layout/item_letter_index_title.xml @@ -0,0 +1,11 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/list_select_friend.xml b/app/src/main/res/layout/list_select_friend.xml new file mode 100644 index 0000000000000000000000000000000000000000..2b6f7f0a90701bfed5ffbd762f79bc2c40f033c7 --- /dev/null +++ b/app/src/main/res/layout/list_select_friend.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 01c26a29d994524dbda2f32f163a230183acd8c7..52c31a037a6982ff61e4c70f9d76cff8f8eded79 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -10,5 +10,14 @@ - + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 4df8e5e3f0172a373d867a0655cee1401007e561..1599f0125ffa75b814055ed05a79417fb96da3f5 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -90,4 +90,6 @@ #464646 #6baf77 + + #338899a6 diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 58d7807c6b529f9183f5a957c82c77a49da4ba5d..35643f0fb464ee6b22e6d5943015f72486c49d66 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -151,4 +151,7 @@ 0.14 0.11 + 12dp + 20dp + 100dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index eea0b4c9d0bd5bd5511b4404c569c7c4c91e2e3c..46b26bd943e8daf1075656bdd1f7ad2749e2e40e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -303,4 +303,8 @@ 选择团队 我的任务 + SelectUserActivity + + Hello world! + Settings diff --git a/gradle.properties b/gradle.properties index a67972e42e8d5049d34bf26d17dc1643e814d202..9b73b9663e23405dae1fb37d6ce1b0d146564003 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,5 +16,8 @@ # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true +org.gradle.daemon=true +org.gradle.parallel=true + KEYSTORE_PASSWORD=oschina123 KEY_PASSWORD=oschina123 \ No newline at end of file