diff --git a/app/build.gradle b/app/build.gradle index cd87bba..c4889a3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -45,5 +45,6 @@ dependencies { compile 'com.android.support.constraint:constraint-layout:1.0.2' compile 'com.android.support:support-v4:25.3.1' compile 'com.google.android:flexbox:0.3.0' + compile 'com.android.support:recyclerview-v7:25.3.1' testCompile 'junit:junit:4.12' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 273569a..c7afcc6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -25,10 +25,11 @@ android:name=".MainActivity" android:label="@string/app_name" android:launchMode="singleTask" - android:windowSoftInputMode="stateHidden" - android:screenOrientation="portrait"> + android:screenOrientation="portrait" + android:windowSoftInputMode="stateHidden"> + diff --git a/app/src/main/java/com/example/zhiruili/videoconf/MainActivity.java b/app/src/main/java/com/example/zhiruili/videoconf/MainActivity.java index fcf9014..c15bb6e 100644 --- a/app/src/main/java/com/example/zhiruili/videoconf/MainActivity.java +++ b/app/src/main/java/com/example/zhiruili/videoconf/MainActivity.java @@ -3,7 +3,6 @@ import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; -import android.support.design.widget.BottomNavigationView; import android.support.design.widget.Snackbar; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; @@ -13,12 +12,10 @@ import android.view.MenuItem; import android.view.ViewGroup; -import com.example.zhiruili.utils.ViewUtils; import com.example.zhiruili.videoconf.call.account.ILiveHelper; import com.example.zhiruili.videoconf.call.constants.CallResultCode; import com.tencent.callsdk.ILVCallConfig; import com.tencent.callsdk.ILVCallConstants; -import com.tencent.callsdk.ILVCallListener; import com.tencent.callsdk.ILVCallManager; import com.tencent.callsdk.ILVCallNotification; import com.tencent.callsdk.ILVCallNotificationListener; @@ -38,11 +35,14 @@ public final class MainActivity extends AppCompatActivity implements ToCallBufferFragment.OnFragmentInteractionListener, - ILVCallNotificationListener, ILVIncomingListener, ILVCallListener { + RecentCallsFragment.OnFragmentInteractionListener, + ILVCallNotificationListener, ILVIncomingListener { private static final String TAG = MainActivity.class.getSimpleName(); private ViewGroup mMainContainer; + private ToCallBufferFragment mToCallBuffer; + private RecentCallsFragment mRecentCallsList; private static final class RequestCode { public static final int REQ_CALL = 0; @@ -50,26 +50,6 @@ private static final class RequestCode { // 是否登录 private boolean mHasLogin = false; - // 当前导航 tab id - private int mCurrNavItemId = -1; - // 导航 tab 点击事件监听器 - private BottomNavigationView.OnNavigationItemSelectedListener - mOnNavigationItemSelectedListener = item -> { - - Log.d(TAG, "Select item " + item.getItemId()); - int itemId = item.getItemId(); - if (itemId == mCurrNavItemId) { - return false; - } - if (itemId == R.id.navigation_recent_calls) { - - } else if (itemId == R.id.navigation_contacts) { - - } else { - return false; - } - return true; - }; @Override protected void onCreate(Bundle savedInstanceState) { @@ -86,7 +66,6 @@ protected void onCreate(Bundle savedInstanceState) { mHasLogin = intent.getBooleanExtra(getString(R.string.intent_extra_has_login), false); if (mHasLogin) { Log.v(TAG, "User has login"); - initViews(); initCallSdk(); return; } @@ -105,7 +84,6 @@ protected void onCreate(Bundle savedInstanceState) { _ignore -> { Log.v(TAG, "login success"); mHasLogin = true; - initViews(); initCallSdk(); }, err -> { @@ -115,13 +93,8 @@ protected void onCreate(Bundle savedInstanceState) { gotoLogin(); }); } - ViewUtils.hideKeyboard(this); - } - - private void initViews() { - BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation); - navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener); - navigation.setSelectedItemId(R.id.navigation_recent_calls); + mToCallBuffer = (ToCallBufferFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_to_call_buffer); + mRecentCallsList = (RecentCallsFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_recent_calls_list); } @Override @@ -146,11 +119,9 @@ private void initCallSdk() { private void registerCallSdkListeners() { ILVCallManager.getInstance().addIncomingListener(this); - ILVCallManager.getInstance().addCallListener(this); } private void unregisterCallSdkListeners() { - ILVCallManager.getInstance().removeCallListener(this); ILVCallManager.getInstance().removeIncomingListener(this); } @@ -274,6 +245,11 @@ public void onStartCalling(ArrayList calledIds) { gotoCall(ILiveLoginManager.getInstance().getMyUserId(), 0, ILVCallConstants.CALL_TYPE_VIDEO, calledIds); } + @Override + public void onListItemClick(String callId) { + mToCallBuffer.addIdToBuffer(callId); + } + @Override public void onRecvNotification(int callId, ILVCallNotification notification) { Log.d(TAG, "onRecvNotification, callId: " + callId); @@ -282,17 +258,20 @@ public void onRecvNotification(int callId, ILVCallNotification notification) { @Override public void onNewIncomingCall(int callId, int callType, ILVIncomingNotification notification) { Log.d(TAG, "onNewIncomingCall, callId: " + callId + ", callType: " + callType); - Log.d(TAG, "onNewIncomingCall, sponsorId: " + notification.getSponsorId() + ", sender: " + notification.getSender() + ", members: " + notification.getMembersString()); + Log.d(TAG, "onNewIncomingCall, sponsorId: " + notification.getSponsorId() + + ", sender: " + notification.getSender() + + ", members: " + notification.getMembersString()); final String membersMsg; if (notification.getMembersString() == null) { membersMsg = ""; } else { - membersMsg = "\n其他参与者:" + notification.getMembersString(); + membersMsg = "\n参与者:" + notification.getMembersString(); } new AlertDialog.Builder(this) .setTitle("新会议请求") .setMessage("会议发起人:" + notification.getSponsorId() + membersMsg) - .setNegativeButton("拒绝", null) + .setNegativeButton("拒绝", + (dialog, which) -> ILVCallManager.getInstance().rejectCall(callId)) .setPositiveButton("接受", (dialog, which) -> { final ArrayList members = new ArrayList<>(); if (notification.getMembers() != null) { @@ -304,21 +283,13 @@ public void onNewIncomingCall(int callId, int callType, ILVIncomingNotification } private void gotoCall(String sponsor, int callId, int callType, ArrayList members) { - startActivityForResult(CallActivity.createIntent(this, callId, callType, sponsor, members), RequestCode.REQ_CALL); - } - - @Override - public void onCallEstablish(int callId) { - Log.d(TAG, "onCallEstablish, callId: " + callId); - } - - @Override - public void onCallEnd(int callId, int endResult, String endInfo) { - Log.d(TAG, "onCallEnd, callId: " + callId + ", endResult: " + endResult); - } - - @Override - public void onException(int iExceptionId, int errCode, String errMsg) { - Log.d(TAG, "onException, exceptionId: " + iExceptionId + ", errorCode: " + errCode + ", errMsg: " + errMsg); +// mRecentCallsList +// .updateCalls(members, callId != 0) +// .subscribeOn(Schedulers.io()) +// .observeOn(AndroidSchedulers.mainThread()) +// .subscribe(success -> { +// Log.d(TAG, "gotoCall success = " + success); + startActivityForResult(CallActivity.createIntent(this, callId, callType, sponsor, members), RequestCode.REQ_CALL); +// }); } } diff --git a/app/src/main/java/com/example/zhiruili/videoconf/RecentCallsFragment.java b/app/src/main/java/com/example/zhiruili/videoconf/RecentCallsFragment.java new file mode 100644 index 0000000..d368289 --- /dev/null +++ b/app/src/main/java/com/example/zhiruili/videoconf/RecentCallsFragment.java @@ -0,0 +1,117 @@ +package com.example.zhiruili.videoconf; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.example.zhiruili.videoconf.data.RecentCallsDbHelper; +import com.example.zhiruili.videoconf.data.RecentCallsListContract.*; +import com.example.zhiruili.videoconf.data.TestHelper; + +import java.util.List; + +import io.reactivex.Single; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.schedulers.Schedulers; + +public class RecentCallsFragment extends Fragment { + + private OnFragmentInteractionListener mInteractionListener; + private RecentCallsListAdapter mListAdapter = null; + private SQLiteDatabase mDb; + + public RecentCallsFragment() { } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + final View rootView = inflater.inflate(R.layout.fragment_recent_calls, container, false); + final RecyclerView callsListView = (RecyclerView) rootView.findViewById(R.id.rv_recent_calls_list); + callsListView.setLayoutManager(new LinearLayoutManager(rootView.getContext())); + + Single + .fromCallable(() -> new RecentCallsDbHelper(getActivity()).getWritableDatabase()) + .doOnSuccess(db -> mDb = db) + // .doOnSuccess(TestHelper::insertFakeData) + .map(_ignore -> getAllRecentCalls()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .map(cursor -> new RecentCallsListAdapter(rootView.getContext(), cursor, mInteractionListener::onListItemClick)) + .doOnSuccess(adapter -> mListAdapter = adapter) + .doOnSuccess(callsListView::setAdapter) + .doOnError(Throwable::printStackTrace) + .subscribe(); + + return rootView; + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + if (context instanceof OnFragmentInteractionListener) { + mInteractionListener = (OnFragmentInteractionListener) context; + } else { + throw new RuntimeException(context.toString() + + " must implement OnFragmentInteractionListener"); + } + } + + @Override + public void onDetach() { + super.onDetach(); + mInteractionListener = null; + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + } + + public interface OnFragmentInteractionListener { + void onListItemClick(String callId); + } + + public Single updateCalls(List callIds, boolean isCallIn) { + return Single + .fromCallable(() -> { + mDb.beginTransaction(); + for (String id : callIds) { + mDb.delete(RecentCallsListEntry.TABLE_NAME, RecentCallsListEntry.COLUMN_PERSON_ID + "=?", new String[] { id }); + } + for (String id : callIds) { + ContentValues cv = new ContentValues(); + cv.put(RecentCallsListEntry.COLUMN_PERSON_ID, id); + cv.put(RecentCallsListEntry.COLUMN_IS_CALL_IN, isCallIn); + mDb.insert(RecentCallsListEntry.TABLE_NAME, null, cv); + } + mDb.setTransactionSuccessful(); + return true; + }) + .doFinally(() -> mDb.endTransaction()) + .map(_ignore -> getAllRecentCalls()) + .doOnError(Throwable::printStackTrace) + .subscribeOn(Schedulers.io()) + .doOnSuccess(mListAdapter::swapCursor) + .subscribeOn(AndroidSchedulers.mainThread()) + .map(_ignore -> true) + .onErrorReturn(err -> { + err.printStackTrace(); + return false; + }); + } + + public Cursor getAllRecentCalls() { + return mDb.query( + RecentCallsListEntry.TABLE_NAME, + null, null, null, null, null, + RecentCallsListEntry.COLUMN_CALL_TIME + " DESC"); + } +} diff --git a/app/src/main/java/com/example/zhiruili/videoconf/RecentCallsListAdapter.java b/app/src/main/java/com/example/zhiruili/videoconf/RecentCallsListAdapter.java new file mode 100644 index 0000000..2fe6c23 --- /dev/null +++ b/app/src/main/java/com/example/zhiruili/videoconf/RecentCallsListAdapter.java @@ -0,0 +1,76 @@ +package com.example.zhiruili.videoconf; + +import android.content.Context; +import android.database.Cursor; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.example.zhiruili.videoconf.data.RecentCallsListContract.*; + +public class RecentCallsListAdapter extends RecyclerView.Adapter { + + private Context mContext; + private Cursor mCursor; + private OnItemClickListener mClickListener; + + public RecentCallsListAdapter(Context context, Cursor cursor, OnItemClickListener listener) { + mContext = context; + mCursor = cursor; + mClickListener = listener; + } + + @Override + public RecentCallViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + LayoutInflater inflater = LayoutInflater.from(mContext); + View view = inflater.inflate(R.layout.recent_calls_list_item, parent, false); + return new RecentCallViewHolder(view); + } + + @Override + public void onBindViewHolder(RecentCallViewHolder holder, int position) { + if (!mCursor.moveToPosition(position)) { + return; + } + int idxCallId = mCursor.getColumnIndex(RecentCallsListEntry.COLUMN_PERSON_ID); + String callId = mCursor.getString(idxCallId); + int idxIsCallIn = mCursor.getColumnIndex(RecentCallsListEntry.COLUMN_IS_CALL_IN); + boolean isCallIn = mCursor.getInt(idxIsCallIn) != 0; + int idxCallTime = mCursor.getColumnIndex(RecentCallsListEntry.COLUMN_CALL_TIME); + String callTimeString = mCursor.getString(idxCallTime); + holder.callId.setText(callId); + holder.callTime.setText(callTimeString); + holder.callInOrOut.setText(isCallIn ? mContext.getString(R.string.label_call_in) : mContext.getString(R.string.label_call_out)); + } + + void swapCursor(Cursor newCursor) { + mCursor = newCursor; + notifyDataSetChanged(); + } + + @Override + public int getItemCount() { + return mCursor.getCount(); + } + + class RecentCallViewHolder extends RecyclerView.ViewHolder { + + TextView callId; + TextView callTime; + TextView callInOrOut; + + public RecentCallViewHolder(View itemView) { + super(itemView); + callId = (TextView) itemView.findViewById(R.id.tv_call_id); + callTime = (TextView) itemView.findViewById(R.id.tv_call_time); + callInOrOut = (TextView) itemView.findViewById(R.id.tv_call_in_or_out); + itemView.setOnClickListener(v -> mClickListener.onClick(callId.getText().toString())); + } + } + + public interface OnItemClickListener { + void onClick(String callId); + } +} diff --git a/app/src/main/java/com/example/zhiruili/videoconf/data/RecentCallsDbHelper.java b/app/src/main/java/com/example/zhiruili/videoconf/data/RecentCallsDbHelper.java new file mode 100644 index 0000000..0108ce2 --- /dev/null +++ b/app/src/main/java/com/example/zhiruili/videoconf/data/RecentCallsDbHelper.java @@ -0,0 +1,36 @@ +package com.example.zhiruili.videoconf.data; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; + +import com.example.zhiruili.videoconf.data.RecentCallsListContract.*; + +public class RecentCallsDbHelper extends SQLiteOpenHelper { + + private static final String DATABASE_NAME = "recent_calls.db"; + + private static final int DATABASE_VERSION = 1; + + public RecentCallsDbHelper(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + final String sqlCreateTable = + "CREATE TABLE " + RecentCallsListEntry.TABLE_NAME + "(" + + RecentCallsListEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + RecentCallsListEntry.COLUMN_PERSON_ID + " TEXT NOT NULL, " + + RecentCallsListEntry.COLUMN_IS_CALL_IN + " INTEGER NOT NULL, " + + RecentCallsListEntry.COLUMN_CALL_TIME + " TIMESTAMP DEFAULT CURRENT_TIMESTAMP" + + ");"; + db.execSQL(sqlCreateTable); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + db.execSQL("DROP TABLE IF EXISTS " + RecentCallsListEntry.TABLE_NAME); + onCreate(db); + } +} diff --git a/app/src/main/java/com/example/zhiruili/videoconf/data/RecentCallsListContract.java b/app/src/main/java/com/example/zhiruili/videoconf/data/RecentCallsListContract.java new file mode 100644 index 0000000..f7eaf9e --- /dev/null +++ b/app/src/main/java/com/example/zhiruili/videoconf/data/RecentCallsListContract.java @@ -0,0 +1,16 @@ +package com.example.zhiruili.videoconf.data; + +import android.provider.BaseColumns; + +public class RecentCallsListContract { + + private RecentCallsListContract() { } + + public static final class RecentCallsListEntry implements BaseColumns { + + public static final String TABLE_NAME = "recent_calls_list"; + public static final String COLUMN_PERSON_ID = "person_id"; + public static final String COLUMN_CALL_TIME = "call_time"; + public static final String COLUMN_IS_CALL_IN = "is_call_in"; + } +} diff --git a/app/src/main/java/com/example/zhiruili/videoconf/data/TestHelper.java b/app/src/main/java/com/example/zhiruili/videoconf/data/TestHelper.java new file mode 100644 index 0000000..0072fe9 --- /dev/null +++ b/app/src/main/java/com/example/zhiruili/videoconf/data/TestHelper.java @@ -0,0 +1,38 @@ +package com.example.zhiruili.videoconf.data; + +import android.content.ContentValues; +import android.database.SQLException; +import android.database.sqlite.SQLiteDatabase; + +import com.example.zhiruili.videoconf.data.RecentCallsListContract.*; + +import java.util.ArrayList; + +public final class TestHelper { + + public static void insertFakeData(SQLiteDatabase db) { + + ArrayList list = new ArrayList() {{ + for (int i = 10; i < 60; ++i) { + ContentValues cv = new ContentValues(); + cv.put(RecentCallsListEntry.COLUMN_PERSON_ID, "alonglongid_" + i); + cv.put(RecentCallsListEntry.COLUMN_IS_CALL_IN, i % 3 == 0); + cv.put(RecentCallsListEntry.COLUMN_CALL_TIME, "2017-01-01 12:14:" + i); + add(cv); + } + }}; + try { + db.beginTransaction(); + db.delete(RecentCallsListEntry.TABLE_NAME, null, null); + for (ContentValues v : list) { + db.insert(RecentCallsListEntry.TABLE_NAME, null, v); + } + db.setTransactionSuccessful(); + } catch (SQLException e) { + e.printStackTrace(); + } finally { + db.endTransaction(); + } + + } +} diff --git a/app/src/main/res/drawable/ic_format_list_bulleted_black_24dp.xml b/app/src/main/res/drawable/ic_format_list_bulleted_black_24dp.xml deleted file mode 100644 index 6cb93c6..0000000 --- a/app/src/main/res/drawable/ic_format_list_bulleted_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_group_black_24dp.xml b/app/src/main/res/drawable/ic_group_black_24dp.xml deleted file mode 100644 index 4cfd869..0000000 --- a/app/src/main/res/drawable/ic_group_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index b17cb5f..245a3ff 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -19,25 +19,21 @@ android:layout_marginStart="@dimen/activity_horizontal_margin" android:layout_marginEnd="@dimen/activity_horizontal_margin" android:layout_width="0dp" - android:layout_height="wrap_content" /> + android:layout_height="wrap_content" + tools:layout="@layout/fragment_to_call_buffer" /> - - - diff --git a/app/src/main/res/layout/fragment_recent_calls.xml b/app/src/main/res/layout/fragment_recent_calls.xml new file mode 100644 index 0000000..33ecb73 --- /dev/null +++ b/app/src/main/res/layout/fragment_recent_calls.xml @@ -0,0 +1,7 @@ + diff --git a/app/src/main/res/layout/recent_calls_list_item.xml b/app/src/main/res/layout/recent_calls_list_item.xml new file mode 100644 index 0000000..c031d6e --- /dev/null +++ b/app/src/main/res/layout/recent_calls_list_item.xml @@ -0,0 +1,39 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/navigation.xml b/app/src/main/res/menu/navigation.xml deleted file mode 100644 index 090a972..0000000 --- a/app/src/main/res/menu/navigation.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index f0426a8..c1c42ce 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -3,4 +3,5 @@ 16dp 68dp 46dp + 16dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index df484b5..ad3da3c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -42,4 +42,7 @@ 注销成功 无法添加更多的会议成员 + 呼入 + 呼出 +