├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── drawable
│ │ │ │ ├── back.png
│ │ │ │ ├── user.png
│ │ │ │ ├── account.png
│ │ │ │ ├── ic_menu.png
│ │ │ │ ├── setting.png
│ │ │ │ ├── splash.png
│ │ │ │ ├── download.png
│ │ │ │ ├── bubbleleft.9.png
│ │ │ │ ├── bubbleright.9.png
│ │ │ │ ├── shape_round.xml
│ │ │ │ ├── border.xml
│ │ │ │ ├── shape_rectangle.xml
│ │ │ │ └── ic_launcher_background.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── xml
│ │ │ │ └── provider_paths.xml
│ │ │ ├── values
│ │ │ │ ├── colors.xml
│ │ │ │ ├── strings.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ └── styles.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── values-w820dp
│ │ │ │ └── dimens.xml
│ │ │ ├── menu
│ │ │ │ └── menu_main.xml
│ │ │ ├── layout
│ │ │ │ ├── activity_splash.xml
│ │ │ │ ├── fragment_chatroom.xml
│ │ │ │ ├── fragment_userlist.xml
│ │ │ │ ├── fragment_userlistinroom.xml
│ │ │ │ ├── activity_select_user.xml
│ │ │ │ ├── item_select_user.xml
│ │ │ │ ├── activity_view_pager.xml
│ │ │ │ ├── activity_userpw.xml
│ │ │ │ ├── fragment_chat.xml
│ │ │ │ ├── item_user.xml
│ │ │ │ ├── activity_chat.xml
│ │ │ │ ├── activity_login.xml
│ │ │ │ ├── fragment_user.xml
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── item_chatimage_right.xml
│ │ │ │ ├── item_chatroom.xml
│ │ │ │ ├── item_chatmsg_right.xml
│ │ │ │ ├── item_chatimage_left.xml
│ │ │ │ ├── item_chatmsg_left.xml
│ │ │ │ ├── item_chatfile_right.xml
│ │ │ │ └── item_chatfile_left.xml
│ │ │ └── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ ├── java
│ │ │ └── gujc
│ │ │ │ └── directtalk9
│ │ │ │ ├── model
│ │ │ │ ├── NotificationModel.java
│ │ │ │ ├── ChatModel.java
│ │ │ │ ├── UserModel.java
│ │ │ │ ├── ChatRoomModel.java
│ │ │ │ └── Message.java
│ │ │ │ ├── common
│ │ │ │ ├── MyAppGlideModule.java
│ │ │ │ ├── MyFirebaseMessagingService.java
│ │ │ │ ├── Util9.java
│ │ │ │ └── FirestoreAdapter.java
│ │ │ │ ├── photoview
│ │ │ │ ├── HackyViewPager.java
│ │ │ │ └── ViewPagerActivity.java
│ │ │ │ ├── SplashActivity.java
│ │ │ │ ├── UserPWActivity.java
│ │ │ │ ├── chat
│ │ │ │ ├── ChatActivity.java
│ │ │ │ └── SelectUserActivity.java
│ │ │ │ ├── fragment
│ │ │ │ ├── UserListFragment.java
│ │ │ │ ├── UserListInRoomFragment.java
│ │ │ │ ├── UserFragment.java
│ │ │ │ ├── ChatRoomFragment.java
│ │ │ │ └── ChatFragment.java
│ │ │ │ ├── LoginActivity.java
│ │ │ │ └── MainActivity.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── gujc
│ │ │ └── directtalk9
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── gujc
│ │ └── directtalk9
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── Screenshot.png
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── .idea
├── vcs.xml
├── runConfigurations.xml
├── gradle.xml
├── misc.xml
└── codeStyles
│ └── Project.xml
├── gradle.properties
├── README.md
├── gradlew.bat
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/Screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gujc71/DirectTalk9/HEAD/Screenshot.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gujc71/DirectTalk9/HEAD/app/src/main/res/drawable/back.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gujc71/DirectTalk9/HEAD/app/src/main/res/drawable/user.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gujc71/DirectTalk9/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/drawable/account.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gujc71/DirectTalk9/HEAD/app/src/main/res/drawable/account.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gujc71/DirectTalk9/HEAD/app/src/main/res/drawable/ic_menu.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/setting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gujc71/DirectTalk9/HEAD/app/src/main/res/drawable/setting.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gujc71/DirectTalk9/HEAD/app/src/main/res/drawable/splash.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/download.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gujc71/DirectTalk9/HEAD/app/src/main/res/drawable/download.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bubbleleft.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gujc71/DirectTalk9/HEAD/app/src/main/res/drawable/bubbleleft.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bubbleright.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gujc71/DirectTalk9/HEAD/app/src/main/res/drawable/bubbleright.9.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gujc71/DirectTalk9/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gujc71/DirectTalk9/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gujc71/DirectTalk9/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gujc71/DirectTalk9/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gujc71/DirectTalk9/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gujc71/DirectTalk9/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gujc71/DirectTalk9/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gujc71/DirectTalk9/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gujc71/DirectTalk9/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gujc71/DirectTalk9/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/libraries
5 | /.idea/modules.xml
6 | /.idea/workspace.xml
7 | .DS_Store
8 | /build
9 | /captures
10 | .externalNativeBuild
11 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/provider_paths.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Jul 01 08:37:38 KST 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Direct Talk9
3 | Settings
4 | Hello World from section: %1$d
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 16dp
6 | 8dp
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/border.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/app/src/test/java/gujc/directtalk9/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package gujc.directtalk9;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/gujc/directtalk9/model/NotificationModel.java:
--------------------------------------------------------------------------------
1 | package gujc.directtalk9.model;
2 |
3 | public class NotificationModel {
4 | public String to;
5 |
6 | public Notification notification = new Notification();
7 | public Data data = new Data();
8 |
9 | public static class Notification {
10 | public String title;
11 | public String body;
12 | }
13 |
14 | public static class Data {
15 | public String title;
16 | public String body;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_splash.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/java/gujc/directtalk9/model/ChatModel.java:
--------------------------------------------------------------------------------
1 | package gujc.directtalk9.model;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Date;
5 | import java.util.HashMap;
6 | import java.util.List;
7 | import java.util.Map;
8 |
9 | public class ChatModel {
10 | public Map users = new HashMap<>() ;
11 | public Map messages = new HashMap<>() ;
12 |
13 | public static class FileInfo {
14 | public String filename;
15 | public String filesize;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_chatroom.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape_rectangle.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
12 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_userlist.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/main/java/gujc/directtalk9/common/MyAppGlideModule.java:
--------------------------------------------------------------------------------
1 | package gujc.directtalk9.common;
2 |
3 | import android.content.Context;
4 |
5 | import com.bumptech.glide.Glide;
6 | import com.bumptech.glide.Registry;
7 | import com.bumptech.glide.annotation.GlideModule;
8 | import com.bumptech.glide.module.AppGlideModule;
9 | import com.firebase.ui.storage.images.FirebaseImageLoader;
10 | import com.google.firebase.storage.StorageReference;
11 |
12 | import java.io.InputStream;
13 |
14 | @GlideModule
15 | public class MyAppGlideModule extends AppGlideModule {
16 |
17 | @Override
18 | public void registerComponents(Context context, Glide glide, Registry registry) {
19 | // Register FirebaseImageLoader to handle StorageReference
20 | registry.append(StorageReference.class, InputStream.class, new FirebaseImageLoader.Factory());
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_userlistinroom.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/gujc/directtalk9/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package gujc.directtalk9;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("gujc.directtalk9", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_select_user.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
17 |
18 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_select_user.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
20 |
21 |
29 |
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DirectTalk9 #
2 | This is a messenger app for Android based on Firebase (Cloud Firestore and Realtime Database) for live chat.
3 |
4 | This app is suitable for enterprise messenger where all users appears because there is no management function such as add / delete / find friends.
5 |
6 | 
7 |
8 | ### BRANCHES ###
9 | - master: Cloud Firestore
10 | - realtime: Realtime Database
11 |
12 | ### FEATURES ###
13 | - 1:1 and Group chat
14 | - Text, Image, File transfer
15 |
16 | ### INSTALLATION ###
17 | 1. Clone this source from github (in android studio).
18 | 2. Copy google-services.json to /app folder.
19 |
20 | You can get google-services.json from [Firebase Console](https://support.google.com/firebase/answer/7015592?hl=en)
21 |
22 | OR
23 |
24 | in android studio, you can make with Tool > Firebase menu
25 |
26 | 3. Run.
27 |
28 | If you see a message like "Please select Android SDK", modify gradle file and run sync now.
29 |
30 |
31 | 4. To use push server(Google Cloud Messaging), you must put the key provided by Firebase in the request header(Authorization) in the sendGCM function in ChatActivity.java.
32 |
33 | ### License ###
34 | GPL v3
--------------------------------------------------------------------------------
/app/src/main/java/gujc/directtalk9/photoview/HackyViewPager.java:
--------------------------------------------------------------------------------
1 | package gujc.directtalk9.photoview;
2 |
3 | import android.content.Context;
4 | import android.support.v4.view.ViewPager;
5 | import android.util.AttributeSet;
6 | import android.view.MotionEvent;
7 |
8 | /**
9 | * Hacky fix for Issue #4 and
10 | * http://code.google.com/p/android/issues/detail?id=18990
11 | *
12 | * ScaleGestureDetector seems to mess up the touch events, which means that
13 | * ViewGroups which make use of onInterceptTouchEvent throw a lot of
14 | * IllegalArgumentException: pointerIndex out of range.
15 | *
16 | * There's not much I can do in my code for now, but we can mask the result by
17 | * just catching the problem and ignoring it.
18 | *
19 | * @author Chris Banes
20 | */
21 | public class HackyViewPager extends ViewPager {
22 |
23 | public HackyViewPager(Context context) {
24 | super(context);
25 | }
26 |
27 | public HackyViewPager(Context context, AttributeSet attrs) {
28 | super(context, attrs);
29 | }
30 |
31 | @Override
32 | public boolean onInterceptTouchEvent(MotionEvent ev) {
33 | try {
34 | return super.onInterceptTouchEvent(ev);
35 | } catch (IllegalArgumentException e) {
36 | return false;
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/java/gujc/directtalk9/model/UserModel.java:
--------------------------------------------------------------------------------
1 | package gujc.directtalk9.model;
2 |
3 | public class UserModel {
4 | private String userid;
5 | private String uid;
6 | private String usernm;
7 | private String token;
8 | private String userphoto;
9 | private String usermsg;
10 |
11 | public String getUserid() {
12 | return userid;
13 | }
14 |
15 | public void setUserid(String userid) {
16 | this.userid = userid;
17 | }
18 |
19 | public String getUid() {
20 | return uid;
21 | }
22 |
23 | public void setUid(String uid) {
24 | this.uid = uid;
25 | }
26 |
27 | public String getUsernm() {
28 | return usernm;
29 | }
30 |
31 | public void setUsernm(String usernm) {
32 | this.usernm = usernm;
33 | }
34 |
35 | public String getToken() {
36 | return token;
37 | }
38 |
39 | public void setToken(String token) {
40 | this.token = token;
41 | }
42 |
43 | public String getUserphoto() {
44 | return userphoto;
45 | }
46 |
47 | public void setUserphoto(String userphoto) {
48 | this.userphoto = userphoto;
49 | }
50 |
51 | public String getUsermsg() {
52 | return usermsg;
53 | }
54 |
55 | public void setUsermsg(String usermsg) {
56 | this.usermsg = usermsg;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_view_pager.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
23 |
28 |
29 |
30 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_userpw.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
14 |
20 |
21 |
22 |
25 |
31 |
32 |
33 |
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/java/gujc/directtalk9/model/ChatRoomModel.java:
--------------------------------------------------------------------------------
1 | package gujc.directtalk9.model;
2 |
3 | public class ChatRoomModel {
4 | private String roomID;
5 | private String title;
6 | private String photo;
7 | private String lastMsg;
8 | private String lastDatetime;
9 | private Integer userCount;
10 | private Integer unreadCount;
11 |
12 | public String getRoomID() {
13 | return roomID;
14 | }
15 |
16 | public void setRoomID(String roomID) {
17 | this.roomID = roomID;
18 | }
19 |
20 | public String getTitle() {
21 | return title;
22 | }
23 |
24 | public void setTitle(String title) {
25 | this.title = title;
26 | }
27 |
28 | public String getPhoto() {
29 | return photo;
30 | }
31 |
32 | public void setPhoto(String photo) {
33 | this.photo = photo;
34 | }
35 |
36 | public String getLastMsg() {
37 | return lastMsg;
38 | }
39 |
40 | public void setLastMsg(String lastMsg) {
41 | this.lastMsg = lastMsg;
42 | }
43 |
44 | public String getLastDatetime() {
45 | return lastDatetime;
46 | }
47 |
48 | public void setLastDatetime(String lastDatetime) {
49 | this.lastDatetime = lastDatetime;
50 | }
51 |
52 | public Integer getUserCount() {
53 | return userCount;
54 | }
55 |
56 | public void setUserCount(Integer userCount) {
57 | this.userCount = userCount;
58 | }
59 |
60 | public Integer getUnreadCount() {
61 | return unreadCount;
62 | }
63 |
64 | public void setUnreadCount(Integer unreadCount) {
65 | this.unreadCount = unreadCount;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_chat.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
13 |
14 |
19 |
20 |
25 |
26 |
31 |
32 |
38 |
39 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_user.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
22 |
30 |
31 |
43 |
44 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/java/gujc/directtalk9/model/Message.java:
--------------------------------------------------------------------------------
1 | package gujc.directtalk9.model;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Date;
5 | import java.util.HashMap;
6 | import java.util.List;
7 | import java.util.Map;
8 |
9 | public class Message {
10 | private String uid;
11 | private String msg;
12 | private String msgtype; // 0: msg, 1: image, 2: file
13 | private Date timestamp;
14 | private List readUsers = new ArrayList<>();
15 | private String filename;
16 | private String filesize;
17 |
18 | public String getUid() {
19 | return uid;
20 | }
21 |
22 | public void setUid(String uid) {
23 | this.uid = uid;
24 | }
25 |
26 | public String getMsg() {
27 | return msg;
28 | }
29 |
30 | public void setMsg(String msg) {
31 | this.msg = msg;
32 | }
33 |
34 | public String getMsgtype() {
35 | return msgtype;
36 | }
37 |
38 | public void setMsgtype(String msgtype) {
39 | this.msgtype = msgtype;
40 | }
41 |
42 | public Date getTimestamp() {
43 | return timestamp;
44 | }
45 |
46 | public void setTimestamp(Date timestamp) {
47 | this.timestamp = timestamp;
48 | }
49 |
50 | public List getReadUsers() {
51 | return readUsers;
52 | }
53 |
54 | public void setReadUsers(List readUsers) {
55 | this.readUsers = readUsers;
56 | }
57 |
58 | public String getFilename() {
59 | return filename;
60 | }
61 |
62 | public void setFilename(String filename) {
63 | this.filename = filename;
64 | }
65 |
66 | public String getFilesize() {
67 | return filesize;
68 | }
69 |
70 | public void setFilesize(String filesize) {
71 | this.filesize = filesize;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/app/src/main/java/gujc/directtalk9/SplashActivity.java:
--------------------------------------------------------------------------------
1 | package gujc.directtalk9;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.os.Handler;
7 | import android.view.WindowManager;
8 |
9 | import com.google.firebase.auth.FirebaseAuth;
10 | import com.google.firebase.firestore.FirebaseFirestore;
11 | import com.google.firebase.firestore.FirebaseFirestoreSettings;
12 |
13 | public class SplashActivity extends Activity {
14 | private final int SPLASH_DISPLAY_LENGTH = 100;
15 |
16 | @Override
17 | protected void onCreate(Bundle savedInstanceState) {
18 | super.onCreate(savedInstanceState);
19 | setContentView(R.layout.activity_splash);
20 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
21 |
22 | //FirebaseAuth.getInstance().signOut();
23 | FirebaseFirestore firestore = FirebaseFirestore.getInstance();
24 | FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder()
25 | .setTimestampsInSnapshotsEnabled(true)
26 | .build();
27 | firestore.setFirestoreSettings(settings);
28 |
29 | new Handler().postDelayed(new Runnable(){
30 | @Override
31 | public void run() {
32 | Intent mainIntent = null;
33 | if (FirebaseAuth.getInstance().getCurrentUser()==null) {
34 | mainIntent = new Intent(SplashActivity.this, LoginActivity.class);
35 | } else {
36 | mainIntent = new Intent(SplashActivity.this, MainActivity.class);
37 | }
38 | SplashActivity.this.startActivity(mainIntent);
39 | SplashActivity.this.finish();
40 | }
41 | }, SPLASH_DISPLAY_LENGTH);
42 | }
43 | }
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 27
5 | defaultConfig {
6 | applicationId "gujc.directtalk9"
7 | minSdkVersion 16
8 | targetSdkVersion 27
9 | versionCode 1
10 | versionName "1.0"
11 | multiDexEnabled true
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | buildToolsVersion "27.0.3"
21 | }
22 |
23 | dependencies {
24 | implementation fileTree(dir: 'libs', include: ['*.jar'])
25 | implementation 'com.android.support:appcompat-v7:27.1.1'
26 | implementation 'com.android.support:design:27.1.1'
27 | implementation 'com.android.support.constraint:constraint-layout:1.1.2'
28 |
29 | implementation 'com.google.firebase:firebase-auth:15.0.0'
30 | implementation 'com.google.firebase:firebase-messaging:15.0.2'
31 | implementation 'com.google.firebase:firebase-storage:15.0.2'
32 | implementation 'com.google.firebase:firebase-firestore:17.1.0'
33 | implementation 'com.firebaseui:firebase-ui-storage:4.1.0'
34 |
35 | implementation 'com.google.code.gson:gson:2.8.5'
36 | implementation 'com.squareup.okhttp3:okhttp:3.10.0'
37 | implementation 'com.github.chrisbanes:PhotoView:2.1.4'
38 |
39 | implementation 'com.github.bumptech.glide:glide:4.7.1'
40 | annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1'
41 |
42 | testImplementation 'junit:junit:4.12'
43 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
44 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
45 | }
46 |
47 | apply plugin: 'com.google.gms.google-services'
48 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_chat.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
21 |
22 |
23 |
28 |
31 |
32 |
36 |
37 |
38 |
39 |
40 |
41 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_login.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
16 |
17 |
20 |
26 |
27 |
28 |
31 |
37 |
38 |
43 |
48 |
49 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_user.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
17 |
18 |
21 |
26 |
27 |
28 |
31 |
37 |
38 |
39 |
42 |
47 |
48 |
49 |
54 |
55 |
60 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/app/src/main/java/gujc/directtalk9/UserPWActivity.java:
--------------------------------------------------------------------------------
1 | package gujc.directtalk9;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.NonNull;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.os.Bundle;
7 | import android.view.View;
8 | import android.view.inputmethod.InputMethodManager;
9 | import android.widget.Button;
10 | import android.widget.EditText;
11 |
12 | import com.google.android.gms.tasks.OnCompleteListener;
13 | import com.google.android.gms.tasks.Task;
14 | import com.google.firebase.auth.FirebaseAuth;
15 | import com.google.firebase.auth.FirebaseUser;
16 |
17 | import gujc.directtalk9.common.Util9;
18 |
19 | public class UserPWActivity extends AppCompatActivity {
20 | private EditText user_pw1;
21 | private EditText user_pw2;
22 | private Button saveBtn;
23 |
24 | @Override
25 | protected void onCreate(Bundle savedInstanceState) {
26 | super.onCreate(savedInstanceState);
27 | setContentView(R.layout.activity_userpw);
28 |
29 | user_pw1 = findViewById(R.id.user_pw1);
30 | user_pw2 = findViewById(R.id.user_pw2);
31 | saveBtn = findViewById(R.id.saveBtn);
32 | saveBtn.setOnClickListener(saveBtnClickListener);
33 | }
34 |
35 |
36 | Button.OnClickListener saveBtnClickListener = new View.OnClickListener() {
37 | public void onClick(final View view) {
38 | String pw1 = user_pw1.getText().toString().trim();
39 | if (pw1.length()<8) {
40 | Util9.showMessage(getApplicationContext(), "Please enter at least eight characters.");
41 | return;
42 | }
43 | if (!pw1.equals(user_pw2.getText().toString().trim())) {
44 | Util9.showMessage(getApplicationContext(), "Password does not match the confirm password.");
45 | return;
46 | }
47 |
48 | FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
49 | user.updatePassword(pw1).addOnCompleteListener(new OnCompleteListener() {
50 | @Override
51 | public void onComplete(@NonNull Task task) {
52 | Util9.showMessage(getApplicationContext(), "Password changed");
53 |
54 | InputMethodManager imm= (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
55 | imm.hideSoftInputFromWindow(user_pw2.getWindowToken(), 0);
56 |
57 | onBackPressed();
58 | }
59 | });
60 | }
61 | };
62 | }
63 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
50 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/app/src/main/java/gujc/directtalk9/common/MyFirebaseMessagingService.java:
--------------------------------------------------------------------------------
1 | package gujc.directtalk9.common;
2 |
3 | import android.app.Notification;
4 | import android.app.NotificationChannel;
5 | import android.app.NotificationManager;
6 | import android.app.PendingIntent;
7 | import android.content.Context;
8 | import android.content.Intent;
9 | import android.media.RingtoneManager;
10 | import android.net.Uri;
11 | import android.os.Build;
12 | import android.support.v4.app.NotificationCompat;
13 |
14 | import com.google.firebase.messaging.FirebaseMessagingService;
15 | import com.google.firebase.messaging.RemoteMessage;
16 |
17 | import gujc.directtalk9.MainActivity;
18 | import gujc.directtalk9.R;
19 |
20 | public class MyFirebaseMessagingService extends FirebaseMessagingService {
21 |
22 | @Override
23 | public void onMessageReceived(RemoteMessage remoteMessage) {
24 | if (remoteMessage.getData().size() > 0) {
25 | String title = remoteMessage.getData().get("title").toString();
26 | String body = remoteMessage.getData().get("body").toString();
27 | sendNotification(body);
28 | }
29 | }
30 |
31 | private void sendNotification(String messageBody) {
32 | Intent intent = new Intent(this, MainActivity.class);
33 | intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
34 | PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent, PendingIntent.FLAG_ONE_SHOT);
35 |
36 | String channelId = "fcm_default_channel";//getString(R.string.default_notification_channel_id);
37 | Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
38 | NotificationCompat.Builder notificationBuilder =
39 | new NotificationCompat.Builder(this, channelId)
40 | .setSmallIcon(R.drawable.splash)
41 | .setContentTitle("FCM Message")
42 | .setContentText(messageBody)
43 | .setAutoCancel(true)
44 | .setSound(defaultSoundUri)
45 | .setPriority(Notification.PRIORITY_HIGH)
46 | .setContentIntent(pendingIntent);
47 |
48 | NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
49 |
50 | // Since android Oreo notification channel is needed.
51 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
52 | NotificationChannel channel = new NotificationChannel(channelId,
53 | "Channel human readable title",
54 | NotificationManager.IMPORTANCE_DEFAULT);
55 | notificationManager.createNotificationChannel(channel);
56 | }
57 |
58 | notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
17 |
18 |
27 |
28 |
29 |
30 |
34 |
35 |
40 |
41 |
46 |
47 |
52 |
53 |
54 |
55 |
56 |
61 |
62 |
70 |
71 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_chatimage_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
22 |
23 |
30 |
36 |
37 |
38 |
39 |
45 |
46 |
50 |
51 |
60 |
61 |
67 |
68 |
69 |
73 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/app/src/main/java/gujc/directtalk9/common/Util9.java:
--------------------------------------------------------------------------------
1 | package gujc.directtalk9.common;
2 |
3 | import android.Manifest;
4 | import android.app.Activity;
5 | import android.content.Context;
6 | import android.content.pm.PackageManager;
7 | import android.os.Build;
8 | import android.os.Environment;
9 | import android.support.v4.app.ActivityCompat;
10 | import android.util.Log;
11 | import android.view.Gravity;
12 | import android.view.View;
13 | import android.view.inputmethod.InputMethodManager;
14 | import android.widget.Toast;
15 |
16 | import java.text.SimpleDateFormat;
17 | import java.util.Date;
18 |
19 | public class Util9 {
20 | private static final Util9 ourInstance = new Util9();
21 |
22 | static Util9 getInstance() {
23 | return ourInstance;
24 | }
25 |
26 | private Util9() {
27 | }
28 |
29 | public static void showMessage(Context context, String msg) {
30 | Toast toast = Toast.makeText(context, msg, Toast.LENGTH_SHORT);
31 | toast.setGravity(Gravity.CENTER, 0, 0);
32 | toast.show();
33 | }
34 |
35 | public static void hideKeyboard(Activity activity) {
36 | InputMethodManager imm = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
37 | View view = activity.getCurrentFocus();
38 | if (view == null) {
39 | view = new View(activity);
40 | }
41 | imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
42 | }
43 |
44 | public static String getUniqueValue() {
45 | SimpleDateFormat ft = new SimpleDateFormat("yyyyMMddhhmmssSSS");
46 | return ft.format(new Date()) + (int) (Math.random()*10);
47 | }
48 |
49 | public static String size2String(Long filesize) {
50 | Integer unit = 1024;
51 | if (filesize < unit){
52 | return String.format("%d bytes", filesize);
53 | }
54 | int exp = (int) (Math.log(filesize) / Math.log(unit));
55 |
56 | return String.format("%.0f %sbytes", filesize / Math.pow(unit, exp), "KMGTPE".charAt(exp-1));
57 | }
58 |
59 | public static String getRootPath() {
60 | String sdPath;
61 | String ext1 = Environment.getExternalStorageState();
62 | if (ext1.equals(Environment.MEDIA_MOUNTED)) {
63 | sdPath = Environment.getExternalStorageDirectory().getAbsolutePath();
64 | } else {
65 | sdPath = Environment.MEDIA_UNMOUNTED;
66 | }
67 | return sdPath;
68 | }
69 |
70 | public static boolean isPermissionGranted(Activity activity, String permission) {
71 | if (Build.VERSION.SDK_INT >= 23) {
72 | if (activity.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED) {
73 | Log.v("DirectTalk9","Permission is granted");
74 | return true;
75 | } else {
76 | Log.v("DirectTalk9","Permission is revoked");
77 | ActivityCompat.requestPermissions(activity, new String[]{permission}, 1);
78 | return false;
79 | }
80 | }
81 | else { //permission is automatically granted on sdk<23 upon installation
82 | Log.v("DirectTalk9","Permission is granted");
83 | return true;
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_chatroom.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
18 |
23 |
30 |
31 |
41 |
42 |
43 |
50 |
51 |
59 |
60 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_chatmsg_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
22 |
23 |
30 |
36 |
37 |
38 |
39 |
45 |
46 |
50 |
51 |
59 |
60 |
66 |
67 |
68 |
72 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/app/src/main/java/gujc/directtalk9/chat/ChatActivity.java:
--------------------------------------------------------------------------------
1 | package gujc.directtalk9.chat;
2 |
3 | import android.os.Bundle;
4 | import android.support.v4.widget.DrawerLayout;
5 | import android.support.v7.app.ActionBar;
6 | import android.support.v7.app.AppCompatActivity;
7 | import android.support.v7.widget.Toolbar;
8 | import android.view.Gravity;
9 | import android.view.MenuItem;
10 | import android.view.View;
11 | import gujc.directtalk9.R;
12 | import gujc.directtalk9.fragment.ChatFragment;
13 | import gujc.directtalk9.fragment.UserListInRoomFragment;
14 | import gujc.directtalk9.model.ChatModel;
15 |
16 | public class ChatActivity extends AppCompatActivity {
17 | private DrawerLayout drawerLayout;
18 | private ChatFragment chatFragment;
19 | private UserListInRoomFragment userListInRoomFragment = null;
20 |
21 | @Override
22 | protected void onCreate(Bundle savedInstanceState) {
23 | super.onCreate(savedInstanceState);
24 | setContentView(R.layout.activity_chat);
25 |
26 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
27 | setSupportActionBar(toolbar);
28 | ActionBar actionBar = getSupportActionBar();
29 | actionBar.setDisplayHomeAsUpEnabled(true);
30 | actionBar.setHomeButtonEnabled(true);
31 |
32 | String toUid = getIntent().getStringExtra("toUid");
33 | final String roomID = getIntent().getStringExtra("roomID");
34 | String roomTitle = getIntent().getStringExtra("roomTitle");
35 | if (roomTitle!=null) {
36 | actionBar.setTitle(roomTitle);
37 | }
38 |
39 | // left drawer
40 | drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
41 | findViewById(R.id.rightMenuBtn).setOnClickListener(new View.OnClickListener() {
42 | @Override
43 | public void onClick(View v) {
44 | if (drawerLayout.isDrawerOpen(Gravity.RIGHT)) {
45 | drawerLayout.closeDrawer(Gravity.RIGHT);
46 | } else {
47 | if (userListInRoomFragment==null) {
48 | userListInRoomFragment = UserListInRoomFragment.getInstance(roomID, chatFragment.getUserList());
49 | getSupportFragmentManager()
50 | .beginTransaction()
51 | .replace(R.id.drawerFragment, userListInRoomFragment)
52 | .commit();
53 | }
54 | drawerLayout.openDrawer(Gravity.RIGHT);
55 | }
56 | }
57 | });
58 | // chatting area
59 | chatFragment = ChatFragment.getInstance(toUid, roomID);
60 | getSupportFragmentManager()
61 | .beginTransaction()
62 | .replace(R.id.mainFragment, chatFragment )
63 | .commit();
64 | }
65 |
66 | @Override
67 | public boolean onOptionsItemSelected(MenuItem item) {
68 | switch (item.getItemId()){
69 | case android.R.id.home:
70 | onBackPressed();
71 | return true;
72 | default:
73 | return super.onOptionsItemSelected(item);
74 | }
75 | }
76 |
77 | @Override
78 | public void onBackPressed() {
79 | chatFragment.backPressed();
80 | finish();;
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_chatimage_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
21 |
22 |
29 |
35 |
36 |
37 |
38 |
43 |
48 |
49 |
53 |
54 |
55 |
59 |
65 |
66 |
74 |
75 |
76 |
79 |
80 |
89 |
90 |
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_chatmsg_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
21 |
22 |
29 |
35 |
36 |
37 |
38 |
43 |
48 |
49 |
53 |
54 |
55 |
59 |
65 |
66 |
77 |
78 |
79 |
82 |
83 |
92 |
93 |
99 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_chatfile_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
22 |
23 |
30 |
36 |
37 |
38 |
39 |
45 |
46 |
50 |
51 |
60 |
61 |
67 |
68 |
69 |
76 |
85 |
91 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/app/src/main/java/gujc/directtalk9/common/FirestoreAdapter.java:
--------------------------------------------------------------------------------
1 | package gujc.directtalk9.common;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.util.Log;
5 |
6 | import com.google.firebase.firestore.DocumentChange;
7 | import com.google.firebase.firestore.DocumentSnapshot;
8 | import com.google.firebase.firestore.EventListener;
9 | import com.google.firebase.firestore.FirebaseFirestoreException;
10 | import com.google.firebase.firestore.ListenerRegistration;
11 | import com.google.firebase.firestore.Query;
12 | import com.google.firebase.firestore.QuerySnapshot;
13 |
14 | import java.util.ArrayList;
15 |
16 | /**
17 | * RecyclerView adapter for displaying the results of a Firestore {@link Query}.
18 | *
19 | * Note that this class forgoes some efficiency to gain simplicity. For example, the result of
20 | * {@link DocumentSnapshot#toObject(Class)} is not cached so the same object may be deserialized
21 | * many times as the user scrolls.
22 | */
23 | public abstract class FirestoreAdapter
24 | extends RecyclerView.Adapter
25 | implements EventListener {
26 |
27 | private static final String TAG = "FirestoreAdapter";
28 |
29 | private Query mQuery;
30 | private ListenerRegistration mRegistration;
31 |
32 | private ArrayList mSnapshots = new ArrayList<>();
33 |
34 | public FirestoreAdapter(Query query) {
35 | mQuery = query;
36 | }
37 |
38 | @Override
39 | public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {
40 | if (e != null) {
41 | Log.w(TAG, "onEvent:error", e);
42 | onError(e);
43 | return;
44 | }
45 |
46 | // Dispatch the event
47 | Log.d(TAG, "onEvent:numChanges:" + documentSnapshots.getDocumentChanges().size());
48 | for (DocumentChange change : documentSnapshots.getDocumentChanges()) {
49 | switch (change.getType()) {
50 | case ADDED:
51 | onDocumentAdded(change);
52 | break;
53 | case MODIFIED:
54 | onDocumentModified(change);
55 | break;
56 | case REMOVED:
57 | onDocumentRemoved(change);
58 | break;
59 | }
60 | }
61 |
62 | onDataChanged();
63 | }
64 |
65 | public void startListening() {
66 | if (mQuery != null && mRegistration == null) {
67 | mRegistration = mQuery.addSnapshotListener(this);
68 | }
69 | }
70 |
71 | public void stopListening() {
72 | if (mRegistration != null) {
73 | mRegistration.remove();
74 | mRegistration = null;
75 | }
76 |
77 | mSnapshots.clear();
78 | notifyDataSetChanged();
79 | }
80 |
81 | public void setQuery(Query query) {
82 | // Stop listening
83 | stopListening();
84 |
85 | // Clear existing data
86 | mSnapshots.clear();
87 | notifyDataSetChanged();
88 |
89 | // Listen to new query
90 | mQuery = query;
91 | startListening();
92 | }
93 |
94 | @Override
95 | public int getItemCount() {
96 | return mSnapshots.size();
97 | }
98 |
99 | protected DocumentSnapshot getSnapshot(int index) {
100 | return mSnapshots.get(index);
101 | }
102 |
103 | protected void onDocumentAdded(DocumentChange change) {
104 | mSnapshots.add(change.getNewIndex(), change.getDocument());
105 | notifyItemInserted(change.getNewIndex());
106 | }
107 |
108 | protected void onDocumentModified(DocumentChange change) {
109 | if (change.getOldIndex() == change.getNewIndex()) {
110 | // Item changed but remained in same position
111 | mSnapshots.set(change.getOldIndex(), change.getDocument());
112 | notifyItemChanged(change.getOldIndex());
113 | } else {
114 | // Item changed and changed position
115 | mSnapshots.remove(change.getOldIndex());
116 | mSnapshots.add(change.getNewIndex(), change.getDocument());
117 | notifyItemMoved(change.getOldIndex(), change.getNewIndex());
118 | }
119 | }
120 |
121 | protected void onDocumentRemoved(DocumentChange change) {
122 | mSnapshots.remove(change.getOldIndex());
123 | notifyItemRemoved(change.getOldIndex());
124 | }
125 |
126 | protected void onError(FirebaseFirestoreException e) {
127 | Log.w(TAG, "onError", e);
128 | };
129 |
130 | protected void onDataChanged() {}
131 | }
132 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_chatfile_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
21 |
22 |
29 |
35 |
36 |
37 |
38 |
43 |
48 |
49 |
53 |
54 |
55 |
59 |
65 |
66 |
73 |
82 |
88 |
97 |
98 |
99 |
100 |
103 |
104 |
113 |
114 |
120 |
121 |
122 |
123 |
--------------------------------------------------------------------------------
/app/src/main/java/gujc/directtalk9/fragment/UserListFragment.java:
--------------------------------------------------------------------------------
1 | package gujc.directtalk9.fragment;
2 |
3 | import android.support.v4.app.Fragment;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.support.annotation.NonNull;
7 | import android.support.annotation.Nullable;
8 | import android.support.v7.widget.LinearLayoutManager;
9 | import android.support.v7.widget.RecyclerView;
10 | import android.view.LayoutInflater;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.widget.ImageView;
14 | import android.widget.TextView;
15 |
16 | import com.bumptech.glide.Glide;
17 | import com.bumptech.glide.load.resource.bitmap.CenterCrop;
18 | import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
19 | import com.bumptech.glide.request.RequestOptions;
20 | import gujc.directtalk9.R;
21 | import gujc.directtalk9.chat.ChatActivity;
22 | import gujc.directtalk9.common.FirestoreAdapter;
23 | import gujc.directtalk9.model.UserModel;
24 |
25 | import com.google.firebase.auth.FirebaseAuth;
26 | import com.google.firebase.firestore.DocumentSnapshot;
27 | import com.google.firebase.firestore.FirebaseFirestore;
28 | import com.google.firebase.firestore.Query;
29 | import com.google.firebase.storage.FirebaseStorage;
30 | import com.google.firebase.storage.StorageReference;
31 |
32 | public class UserListFragment extends Fragment {
33 | private FirestoreAdapter firestoreAdapter;
34 |
35 | public UserListFragment() {
36 | }
37 |
38 | @Override
39 | public void onStart() {
40 | super.onStart();
41 | if (firestoreAdapter != null) {
42 | firestoreAdapter.startListening();
43 | }
44 | }
45 |
46 | @Override
47 | public void onStop() {
48 | super.onStop();
49 | if (firestoreAdapter != null) {
50 | firestoreAdapter.stopListening();
51 | }
52 | }
53 |
54 | @Nullable
55 | @Override
56 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
57 | View view = inflater.inflate(R.layout.fragment_userlist, container, false);
58 |
59 | firestoreAdapter = new RecyclerViewAdapter(FirebaseFirestore.getInstance().collection("users").orderBy("usernm"));
60 |
61 | RecyclerView recyclerView = view.findViewById(R.id.recyclerView);
62 | recyclerView.setLayoutManager( new LinearLayoutManager((inflater.getContext())));
63 | recyclerView.setAdapter(firestoreAdapter);
64 |
65 | return view;
66 | }
67 |
68 | class RecyclerViewAdapter extends FirestoreAdapter {
69 | final private RequestOptions requestOptions = new RequestOptions().transforms(new CenterCrop(), new RoundedCorners(90));
70 | private StorageReference storageReference;
71 | private String myUid = FirebaseAuth.getInstance().getCurrentUser().getUid();
72 |
73 | RecyclerViewAdapter(Query query) {
74 | super(query);
75 | storageReference = FirebaseStorage.getInstance().getReference();
76 | }
77 |
78 | @Override
79 | public CustomViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
80 | return new CustomViewHolder(LayoutInflater.from(parent.getContext())
81 | .inflate(R.layout.item_user, parent, false));
82 | }
83 |
84 | @Override
85 | public void onBindViewHolder(CustomViewHolder viewHolder, int position) {
86 | DocumentSnapshot documentSnapshot = getSnapshot(position);
87 | final UserModel user = documentSnapshot.toObject(UserModel.class);
88 |
89 | if (myUid.equals(user.getUid())) {
90 | viewHolder.itemView.setVisibility(View.INVISIBLE);
91 | viewHolder.itemView.getLayoutParams().height = 0;
92 | return;
93 | }
94 | viewHolder.user_name.setText(user.getUsernm());
95 | viewHolder.user_msg.setText(user.getUsermsg());
96 |
97 | if (user.getUserphoto()==null) {
98 | Glide.with(getActivity()).load(R.drawable.user)
99 | .apply(requestOptions)
100 | .into(viewHolder.user_photo);
101 | } else{
102 | Glide.with(getActivity())
103 | .load(storageReference.child("userPhoto/"+user.getUserphoto()))
104 | .apply(requestOptions)
105 | .into(viewHolder.user_photo);
106 | }
107 |
108 | viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
109 | @Override
110 | public void onClick(View v) {
111 | Intent intent = new Intent(getView().getContext(), ChatActivity.class);
112 | intent.putExtra("toUid", user.getUid());
113 | startActivity(intent);
114 | }
115 | });
116 |
117 | }
118 | }
119 |
120 | private class CustomViewHolder extends RecyclerView.ViewHolder {
121 | public ImageView user_photo;
122 | public TextView user_name;
123 | public TextView user_msg;
124 |
125 | CustomViewHolder(View view) {
126 | super(view);
127 | user_photo = view.findViewById(R.id.user_photo);
128 | user_name = view.findViewById(R.id.user_name);
129 | user_msg = view.findViewById(R.id.user_msg);
130 | }
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/app/src/main/java/gujc/directtalk9/fragment/UserListInRoomFragment.java:
--------------------------------------------------------------------------------
1 | package gujc.directtalk9.fragment;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.support.annotation.NonNull;
6 | import android.support.annotation.Nullable;
7 | import android.support.v4.app.Fragment;
8 | import android.support.v7.widget.LinearLayoutManager;
9 | import android.support.v7.widget.RecyclerView;
10 | import android.view.LayoutInflater;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.widget.ImageView;
14 | import android.widget.TextView;
15 |
16 | import com.bumptech.glide.Glide;
17 | import com.bumptech.glide.load.resource.bitmap.CenterCrop;
18 | import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
19 | import com.bumptech.glide.request.RequestOptions;
20 | import com.google.firebase.storage.FirebaseStorage;
21 | import com.google.firebase.storage.StorageReference;
22 |
23 | import java.util.ArrayList;
24 | import java.util.List;
25 | import java.util.Map;
26 |
27 | import gujc.directtalk9.R;
28 | import gujc.directtalk9.chat.SelectUserActivity;
29 | import gujc.directtalk9.model.UserModel;
30 |
31 | public class UserListInRoomFragment extends Fragment {
32 | private String roomID;
33 | private List userModels;
34 | private RecyclerView recyclerView;
35 |
36 | public UserListInRoomFragment() {
37 | }
38 |
39 | public static final UserListInRoomFragment getInstance(String roomID, Map userModels) {
40 | List users = new ArrayList();
41 | for( Map.Entry elem : userModels.entrySet() ){
42 | users.add(elem.getValue());
43 | }
44 |
45 | UserListInRoomFragment f = new UserListInRoomFragment();
46 | f.setUserList(users);
47 | Bundle bdl = new Bundle();
48 | bdl.putString("roomID", roomID);
49 | f.setArguments(bdl);
50 |
51 | return f;
52 | }
53 |
54 | @Nullable
55 | @Override
56 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
57 | View view = inflater.inflate(R.layout.fragment_userlistinroom, container, false);
58 | if (getArguments() != null) {
59 | roomID = getArguments().getString("roomID");
60 | }
61 |
62 | recyclerView = view.findViewById(R.id.recyclerView);
63 | recyclerView.setLayoutManager( new LinearLayoutManager((inflater.getContext())));
64 | recyclerView.setAdapter(new UserFragmentRecyclerViewAdapter());
65 |
66 | view.findViewById(R.id.addContactBtn).setOnClickListener(new View.OnClickListener() {
67 | @Override
68 | public void onClick(final View v) {
69 | Intent intent = new Intent(getActivity(), SelectUserActivity.class);
70 | intent.putExtra("roomID", roomID);
71 | startActivity(intent);
72 | }
73 | });
74 |
75 | return view;
76 | }
77 |
78 | public void setUserList(List users) {
79 | userModels = users;
80 | }
81 |
82 | class UserFragmentRecyclerViewAdapter extends RecyclerView.Adapter{
83 | private StorageReference storageReference;
84 | final private RequestOptions requestOptions = new RequestOptions().transforms(new CenterCrop(), new RoundedCorners(90));
85 |
86 | public UserFragmentRecyclerViewAdapter() {
87 | storageReference = FirebaseStorage.getInstance().getReference();
88 | }
89 |
90 | @NonNull
91 | @Override
92 | public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
93 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_user, parent, false);
94 | return new CustomViewHolder(view);
95 | }
96 |
97 | @Override
98 | public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
99 | final UserModel user = userModels.get(position);
100 | CustomViewHolder customViewHolder = (CustomViewHolder) holder;
101 | customViewHolder.user_name.setText(user.getUsernm());
102 | //customViewHolder.user_msg.setText(user.getUsermsg());
103 |
104 | if (user.getUserphoto()==null) {
105 | Glide.with(getActivity()).load(R.drawable.user)
106 | .apply(requestOptions)
107 | .into(customViewHolder.user_photo);
108 | } else{
109 | Glide.with(getActivity())
110 | .load(storageReference.child("userPhoto/"+user.getUserphoto()))
111 | .apply(requestOptions)
112 | .into(customViewHolder.user_photo);
113 | }
114 | }
115 |
116 | @Override
117 | public int getItemCount() {
118 | return userModels.size();
119 | }
120 | }
121 |
122 | private class CustomViewHolder extends RecyclerView.ViewHolder {
123 | public ImageView user_photo;
124 | public TextView user_name;
125 | public TextView user_msg;
126 |
127 | public CustomViewHolder(View view) {
128 | super(view);
129 | user_photo = view.findViewById(R.id.user_photo);
130 | user_name = view.findViewById(R.id.user_name);
131 | user_msg = view.findViewById(R.id.user_msg);
132 | user_msg.setVisibility(View.GONE);
133 | }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/app/src/main/java/gujc/directtalk9/LoginActivity.java:
--------------------------------------------------------------------------------
1 | package gujc.directtalk9;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.content.SharedPreferences;
6 | import android.os.Bundle;
7 | import android.support.annotation.NonNull;
8 | import android.support.v7.app.AppCompatActivity;
9 | import android.text.TextUtils;
10 | import android.util.Log;
11 | import android.view.View;
12 | import android.widget.Button;
13 | import android.widget.EditText;
14 |
15 | import com.google.android.gms.tasks.OnCompleteListener;
16 | import com.google.android.gms.tasks.OnSuccessListener;
17 | import com.google.android.gms.tasks.Task;
18 | import com.google.firebase.auth.AuthResult;
19 | import com.google.firebase.auth.FirebaseAuth;
20 | import com.google.firebase.firestore.FirebaseFirestore;
21 |
22 | import gujc.directtalk9.common.Util9;
23 | import gujc.directtalk9.model.UserModel;
24 |
25 | public class LoginActivity extends AppCompatActivity {
26 | private EditText user_id;
27 | private EditText user_pw;
28 | SharedPreferences sharedPreferences;
29 |
30 | @Override
31 | protected void onCreate(Bundle savedInstanceState) {
32 | super.onCreate(savedInstanceState);
33 | setContentView(R.layout.activity_login);
34 |
35 | user_id = findViewById(R.id.user_id);
36 | user_pw = findViewById(R.id.user_pw);
37 | Button loginBtn = findViewById(R.id.loginBtn);
38 | Button signupBtn = findViewById(R.id.signupBtn);
39 |
40 | loginBtn.setOnClickListener(loginClick);
41 | signupBtn.setOnClickListener(signupClick);
42 |
43 | sharedPreferences = getSharedPreferences("gujc", Activity.MODE_PRIVATE);
44 | String id = sharedPreferences.getString("user_id", "");
45 | if (!"".equals(id)) {
46 | user_id.setText(id);
47 | }
48 | }
49 |
50 | Button.OnClickListener loginClick = new View.OnClickListener() {
51 | public void onClick(View view) {
52 | if (!validateForm()) return;
53 |
54 | FirebaseAuth.getInstance().signInWithEmailAndPassword(user_id.getText().toString(), user_pw.getText().toString()).addOnCompleteListener(new OnCompleteListener() {
55 | @Override
56 | public void onComplete(@NonNull Task task) {
57 | if (task.isSuccessful()) {
58 | sharedPreferences.edit().putString("user_id", user_id.getText().toString()).commit();
59 | Intent intent = new Intent(LoginActivity.this, MainActivity.class);
60 | startActivity(intent);
61 | finish();
62 | } else {
63 | Util9.showMessage(getApplicationContext(), task.getException().getMessage());
64 | }
65 | }
66 | });
67 | }
68 | };
69 |
70 | Button.OnClickListener signupClick = new View.OnClickListener() {
71 | public void onClick(View view) {
72 | if (!validateForm()) return;
73 | final String id = user_id.getText().toString();
74 |
75 | FirebaseAuth.getInstance().createUserWithEmailAndPassword(id, user_pw.getText().toString()).addOnCompleteListener(new OnCompleteListener() {
76 | @Override
77 | public void onComplete(@NonNull Task task) {
78 | if (task.isSuccessful()) {
79 | sharedPreferences.edit().putString("user_id", id).commit();
80 | final String uid = FirebaseAuth.getInstance().getUid();
81 |
82 | UserModel userModel = new UserModel();
83 | userModel.setUid(uid);
84 | userModel.setUserid(id);
85 | userModel.setUsernm(extractIDFromEmail(id));
86 | userModel.setUsermsg("...");
87 |
88 | FirebaseFirestore db = FirebaseFirestore.getInstance();
89 | db.collection("users").document(uid)
90 | .set(userModel)
91 | .addOnSuccessListener(new OnSuccessListener() {
92 | @Override
93 | public void onSuccess(Void aVoid) {
94 | Intent intent = new Intent(LoginActivity.this, MainActivity.class);
95 | startActivity(intent);
96 | finish();
97 | Log.d(String.valueOf(R.string.app_name), "DocumentSnapshot added with ID: " + uid);
98 | }
99 | });
100 | } else {
101 | Util9.showMessage(getApplicationContext(), task.getException().getMessage());
102 | }
103 | }
104 | });
105 | }
106 | };
107 |
108 | String extractIDFromEmail(String email){
109 | String[] parts = email.split("@");
110 | return parts[0];
111 | }
112 |
113 | private boolean validateForm() {
114 | boolean valid = true;
115 |
116 | String email = user_id.getText().toString();
117 | if (TextUtils.isEmpty(email)) {
118 | user_id.setError("Required.");
119 | valid = false;
120 | } else {
121 | user_id.setError(null);
122 | }
123 |
124 | String password = user_pw.getText().toString();
125 | if (TextUtils.isEmpty(password)) {
126 | user_pw.setError("Required.");
127 | valid = false;
128 | } else {
129 | user_pw.setError(null);
130 | }
131 |
132 | return valid;
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/app/src/main/java/gujc/directtalk9/MainActivity.java:
--------------------------------------------------------------------------------
1 | package gujc.directtalk9;
2 |
3 | import android.content.Intent;
4 | import android.support.design.widget.TabLayout;
5 | import android.support.design.widget.FloatingActionButton;
6 | import android.support.v7.app.AppCompatActivity;
7 | import android.support.v7.widget.Toolbar;
8 |
9 | import android.support.v4.app.Fragment;
10 | import android.support.v4.app.FragmentManager;
11 | import android.support.v4.app.FragmentPagerAdapter;
12 | import android.support.v4.view.ViewPager;
13 | import android.os.Bundle;
14 | import android.view.Menu;
15 | import android.view.MenuItem;
16 | import android.view.View;
17 |
18 | import com.google.firebase.auth.FirebaseAuth;
19 | import com.google.firebase.firestore.FirebaseFirestore;
20 | import com.google.firebase.firestore.SetOptions;
21 | import com.google.firebase.iid.FirebaseInstanceId;
22 |
23 | import java.util.HashMap;
24 | import java.util.Map;
25 |
26 | import gujc.directtalk9.chat.SelectUserActivity;
27 | import gujc.directtalk9.fragment.ChatRoomFragment;
28 | import gujc.directtalk9.fragment.UserFragment;
29 | import gujc.directtalk9.fragment.UserListFragment;
30 |
31 | public class MainActivity extends AppCompatActivity {
32 |
33 | /**
34 | * The {@link android.support.v4.view.PagerAdapter} that will provide
35 | * fragments for each of the sections. We use a
36 | * {@link FragmentPagerAdapter} derivative, which will keep every
37 | * loaded fragment in memory. If this becomes too memory intensive, it
38 | * may be best to switch to a
39 | * {@link android.support.v4.app.FragmentStatePagerAdapter}.
40 | */
41 | private SectionsPagerAdapter mSectionsPagerAdapter;
42 |
43 | /**
44 | * The {@link ViewPager} that will host the section contents.
45 | */
46 | private ViewPager mViewPager;
47 |
48 | private FloatingActionButton makeRoomBtn;
49 |
50 | @Override
51 | protected void onCreate(Bundle savedInstanceState) {
52 | super.onCreate(savedInstanceState);
53 | setContentView(R.layout.activity_main);
54 |
55 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
56 | setSupportActionBar(toolbar);
57 | // Create the adapter that will return a fragment for each of the three
58 | // primary sections of the activity.
59 | mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
60 |
61 | // Set up the ViewPager with the sections adapter.
62 | mViewPager = (ViewPager) findViewById(R.id.container);
63 | mViewPager.setAdapter(mSectionsPagerAdapter);
64 |
65 | TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
66 | tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
67 | @Override
68 | public void onTabSelected(TabLayout.Tab tab) {
69 | if (tab.getPosition()==1) { // char room
70 | makeRoomBtn.setVisibility(View.VISIBLE);
71 | }
72 | }
73 |
74 | @Override
75 | public void onTabUnselected(TabLayout.Tab tab) {
76 | makeRoomBtn.setVisibility(View.INVISIBLE);
77 | }
78 |
79 | @Override
80 | public void onTabReselected(TabLayout.Tab tab) {
81 |
82 | }
83 | });
84 |
85 | mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
86 | tabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mViewPager));
87 |
88 | sendRegistrationToServer();
89 |
90 |
91 | makeRoomBtn = findViewById(R.id.makeRoomBtn);
92 | makeRoomBtn.setVisibility(View.INVISIBLE);
93 | makeRoomBtn.setOnClickListener( new View.OnClickListener(){
94 |
95 | @Override
96 | public void onClick(View v) {
97 | startActivity(new Intent(v.getContext(), SelectUserActivity.class));
98 | }
99 | });
100 | }
101 |
102 | void sendRegistrationToServer() {
103 | String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
104 | String token = FirebaseInstanceId.getInstance().getToken();
105 | Map map = new HashMap<>();
106 | map.put("token", token);
107 | FirebaseFirestore.getInstance().collection("users").document(uid).set(map, SetOptions.merge());
108 | }
109 |
110 | @Override
111 | public boolean onCreateOptionsMenu(Menu menu) {
112 | // Inflate the menu; this adds items to the action bar if it is present.
113 | getMenuInflater().inflate(R.menu.menu_main, menu);
114 | return true;
115 | }
116 |
117 | @Override
118 | public boolean onOptionsItemSelected(MenuItem item) {
119 | // Handle action bar item clicks here. The action bar will
120 | // automatically handle clicks on the Home/Up button, so long
121 | // as you specify a parent activity in AndroidManifest.xml.
122 | int id = item.getItemId();
123 |
124 | //noinspection SimplifiableIfStatement
125 | if (id == R.id.action_logout) {
126 | FirebaseAuth.getInstance().signOut();
127 | Intent intent = new Intent(this, LoginActivity.class);
128 | this.startActivity(intent);
129 | this.finish();
130 |
131 | return true;
132 | }
133 |
134 | return super.onOptionsItemSelected(item);
135 | }
136 |
137 | /**
138 | * A {@link FragmentPagerAdapter} that returns a fragment corresponding to
139 | * one of the sections/tabs/pages.
140 | */
141 | public class SectionsPagerAdapter extends FragmentPagerAdapter {
142 |
143 | public SectionsPagerAdapter(FragmentManager fm) {
144 | super(fm);
145 | }
146 |
147 | @Override
148 | public Fragment getItem(int position) {
149 | switch (position) {
150 | case 0: return new UserListFragment();
151 | case 1: return new ChatRoomFragment();
152 | default: return new UserFragment();
153 | }
154 | }
155 |
156 | @Override
157 | public int getCount() {
158 | return 3;
159 | }
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/java/gujc/directtalk9/photoview/ViewPagerActivity.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2011, 2012 Chris Banes.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *******************************************************************************/
16 | package gujc.directtalk9.photoview;
17 |
18 | import android.Manifest;
19 | import android.app.Activity;
20 | import android.os.Bundle;
21 | import android.support.annotation.NonNull;
22 | import android.support.v4.view.PagerAdapter;
23 | import android.support.v4.view.ViewPager;
24 | import android.support.v7.app.ActionBar;
25 | import android.support.v7.app.AppCompatActivity;
26 | import android.support.v7.widget.Toolbar;
27 | import android.util.Log;
28 | import android.view.MenuItem;
29 | import android.view.View;
30 | import android.view.ViewGroup;
31 | import android.view.ViewGroup.LayoutParams;
32 | import android.widget.Button;
33 |
34 | import com.bumptech.glide.Glide;
35 | import com.github.chrisbanes.photoview.PhotoView;
36 | import com.google.android.gms.tasks.OnCompleteListener;
37 | import com.google.android.gms.tasks.OnFailureListener;
38 | import com.google.android.gms.tasks.OnSuccessListener;
39 | import com.google.android.gms.tasks.Task;
40 | import com.google.firebase.firestore.FirebaseFirestore;
41 | import com.google.firebase.firestore.QueryDocumentSnapshot;
42 | import com.google.firebase.firestore.QuerySnapshot;
43 | import com.google.firebase.storage.FileDownloadTask;
44 | import com.google.firebase.storage.FirebaseStorage;
45 | import com.google.firebase.storage.StorageReference;
46 |
47 | import java.io.File;
48 | import java.util.ArrayList;
49 |
50 | import gujc.directtalk9.R;
51 | import gujc.directtalk9.common.Util9;
52 | import gujc.directtalk9.model.Message;
53 |
54 | public class ViewPagerActivity extends AppCompatActivity {
55 |
56 | private static String roomID;
57 | private static String realname;
58 | private static ViewPager viewPager;
59 | private static ArrayList imgList = new ArrayList<>();
60 | private String rootPath = Util9.getRootPath()+"/DirectTalk9/";
61 |
62 | @Override
63 | public void onCreate(Bundle savedInstanceState) {
64 | super.onCreate(savedInstanceState);
65 | setContentView(R.layout.activity_view_pager);
66 | Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar);
67 | setSupportActionBar(toolbar);
68 |
69 | roomID = getIntent().getStringExtra("roomID");
70 | realname = getIntent().getStringExtra("realname");
71 |
72 | viewPager = findViewById(R.id.view_pager);
73 | viewPager.setAdapter(new SamplePagerAdapter());
74 |
75 | findViewById(R.id.downloadBtn).setOnClickListener(downloadBtnClickListener);
76 | //findViewById(R.id.rotateBtn).setOnClickListener(rotateBtnClickListener);
77 |
78 | ActionBar actionBar = getSupportActionBar();
79 | //actionBar.setIcon(R.drawable.back);
80 | actionBar.setTitle("PhotoView");
81 | actionBar.setDisplayHomeAsUpEnabled(true);
82 | actionBar.setHomeButtonEnabled(true);
83 | }
84 |
85 | @Override
86 | public boolean onOptionsItemSelected(MenuItem item)
87 | {
88 | switch (item.getItemId())
89 | {
90 | case android.R.id.home:
91 | onBackPressed();
92 | return true;
93 | default:
94 | return super.onOptionsItemSelected(item);
95 | }
96 | }
97 | Button.OnClickListener downloadBtnClickListener = new View.OnClickListener() {
98 | public void onClick(final View view) {
99 | if (!Util9.isPermissionGranted((Activity) view.getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
100 | return ;
101 | }
102 | Message message = imgList.get(viewPager.getCurrentItem());
103 | /// showProgressDialog("Downloading File.");
104 |
105 | final File localFile = new File(rootPath, message.getFilename());
106 |
107 | // realname == message.msg
108 | FirebaseStorage.getInstance().getReference().child("files/"+message.getMsg()).getFile(localFile).addOnSuccessListener(new OnSuccessListener() {
109 | @Override
110 | public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) {
111 | // hideProgressDialog();
112 | Util9.showMessage(view.getContext(), "Downloaded file");
113 | Log.e("DirectTalk9 ","local file created " +localFile.toString());
114 | }
115 | }).addOnFailureListener(new OnFailureListener() {
116 | @Override
117 | public void onFailure(@NonNull Exception exception) {
118 | Log.e("DirectTalk9 ","local file not created " +exception.toString());
119 | }
120 | });
121 | }
122 | };
123 |
124 | Button.OnClickListener rotateBtnClickListener = new View.OnClickListener() {
125 | public void onClick(View view) {
126 | View child = viewPager.getChildAt(viewPager.getCurrentItem());
127 | PhotoView photoView = child.findViewById(R.id.photoView);
128 | photoView.setRotation(photoView.getRotation()+90);
129 | }
130 | };
131 |
132 | static class SamplePagerAdapter extends PagerAdapter {
133 | private StorageReference storageReference;
134 | private int inx = -1;
135 |
136 | public SamplePagerAdapter() {
137 | storageReference = FirebaseStorage.getInstance().getReference();
138 |
139 | FirebaseFirestore.getInstance().collection("rooms").document(roomID).collection("messages").whereEqualTo("msgtype", "1")
140 | .get()
141 | .addOnCompleteListener(new OnCompleteListener() {
142 | @Override
143 | public void onComplete(@NonNull Task task) {
144 | if (!task.isSuccessful()) { return;}
145 |
146 | for (QueryDocumentSnapshot document : task.getResult()) {
147 | Message message = document.toObject(Message.class);
148 | imgList.add(message);
149 | if (realname.equals(message.getMsg())) {inx = imgList.size()-1; }
150 | }
151 | notifyDataSetChanged();
152 | if (inx>-1) {
153 | viewPager.setCurrentItem(inx);
154 | }
155 | }
156 | });
157 | }
158 |
159 | @Override
160 | public int getCount() {
161 | return imgList.size();
162 | }
163 |
164 | @Override
165 | public View instantiateItem(final ViewGroup container, final int position) {
166 | final PhotoView photoView = new PhotoView(container.getContext());
167 | photoView.setId(R.id.photoView);
168 |
169 | Glide.with(container.getContext())
170 | .load(storageReference.child("filesmall/"+imgList.get(position).getMsg()))
171 | .into(photoView);
172 |
173 | container.addView(photoView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
174 |
175 | return photoView;
176 | }
177 |
178 | @Override
179 | public void destroyItem(ViewGroup container, int position, Object object) {
180 | container.removeView((View) object);
181 | }
182 |
183 | @Override
184 | public boolean isViewFromObject(View view, Object object) {
185 | return view == object;
186 | }
187 |
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/app/src/main/java/gujc/directtalk9/fragment/UserFragment.java:
--------------------------------------------------------------------------------
1 | package gujc.directtalk9.fragment;
2 |
3 | import android.graphics.Bitmap;
4 | import android.support.v4.app.Fragment;
5 | import android.content.Intent;
6 | import android.net.Uri;
7 | import android.os.Bundle;
8 | import android.provider.MediaStore;
9 | import android.support.annotation.NonNull;
10 | import android.support.annotation.Nullable;
11 | import android.text.TextUtils;
12 | import android.view.LayoutInflater;
13 | import android.view.View;
14 | import android.view.ViewGroup;
15 | import android.widget.Button;
16 | import android.widget.EditText;
17 | import android.widget.ImageView;
18 |
19 | import com.bumptech.glide.Glide;
20 | import gujc.directtalk9.R;
21 | import gujc.directtalk9.UserPWActivity;
22 | import gujc.directtalk9.common.Util9;
23 | import gujc.directtalk9.model.UserModel;
24 |
25 | import com.bumptech.glide.request.RequestOptions;
26 | import com.bumptech.glide.request.target.SimpleTarget;
27 | import com.bumptech.glide.request.transition.Transition;
28 | import com.google.android.gms.tasks.OnSuccessListener;
29 | import com.google.firebase.auth.FirebaseAuth;
30 |
31 | import com.google.firebase.firestore.DocumentReference;
32 | import com.google.firebase.firestore.DocumentSnapshot;
33 | import com.google.firebase.firestore.FirebaseFirestore;
34 | import com.google.firebase.storage.FirebaseStorage;
35 |
36 | import java.io.ByteArrayOutputStream;
37 |
38 | public class UserFragment extends Fragment {
39 | private static final int PICK_FROM_ALBUM = 1;
40 | private ImageView user_photo;
41 | private EditText user_id;
42 | private EditText user_name;
43 | private EditText user_msg;
44 |
45 | private UserModel userModel;
46 | private Uri userPhotoUri;
47 |
48 | @Nullable
49 | @Override
50 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
51 | View view = inflater.inflate(R.layout.fragment_user, container, false);
52 |
53 | user_id = view.findViewById(R.id.user_id);
54 | user_id.setEnabled(false);
55 | user_name = view.findViewById(R.id.user_name);
56 | user_msg = view.findViewById(R.id.user_msg);
57 | user_photo = view.findViewById(R.id.user_photo);
58 | user_photo.setOnClickListener(userPhotoIVClickListener);
59 |
60 | Button saveBtn = view.findViewById(R.id.saveBtn);
61 | saveBtn.setOnClickListener(saveBtnClickListener);
62 | Button changePWBtn = view.findViewById(R.id.changePWBtn);
63 | changePWBtn.setOnClickListener(changePWBtnClickListener);
64 |
65 | getUserInfoFromServer();
66 | return view;
67 | }
68 |
69 | void getUserInfoFromServer(){
70 | String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
71 |
72 | DocumentReference docRef = FirebaseFirestore.getInstance().collection("users").document(uid);
73 | docRef.get().addOnSuccessListener(new OnSuccessListener() {
74 | @Override
75 | public void onSuccess(DocumentSnapshot documentSnapshot) {
76 | userModel = documentSnapshot.toObject(UserModel.class);
77 | user_id.setText(userModel.getUserid());
78 | user_name.setText(userModel.getUsernm());
79 | user_msg.setText(userModel.getUsermsg());
80 | if (userModel.getUserphoto()!= null && !"".equals(userModel.getUserphoto())) {
81 | Glide.with(getActivity())
82 | .load(FirebaseStorage.getInstance().getReference("userPhoto/"+userModel.getUserphoto()))
83 | .into(user_photo);
84 | }
85 | }
86 | });
87 | }
88 |
89 | Button.OnClickListener userPhotoIVClickListener = new View.OnClickListener() {
90 | public void onClick(final View view) {
91 | Intent intent = new Intent(Intent.ACTION_PICK);
92 | intent.setType(MediaStore.Images.Media.CONTENT_TYPE);
93 | startActivityForResult(intent, PICK_FROM_ALBUM);
94 | }
95 | };
96 |
97 | @Override
98 | public void onActivityResult(int requestCode, int resultCode, Intent data) {
99 | if (requestCode==PICK_FROM_ALBUM && resultCode== getActivity().RESULT_OK) {
100 | user_photo.setImageURI(data.getData());
101 | userPhotoUri = data.getData();
102 | }
103 | }
104 |
105 | Button.OnClickListener saveBtnClickListener = new View.OnClickListener() {
106 | public void onClick(final View view) {
107 | if (!validateForm()) return;
108 | userModel.setUsernm(user_name.getText().toString());
109 | userModel.setUsermsg(user_msg.getText().toString());
110 |
111 | final String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
112 | final FirebaseFirestore db = FirebaseFirestore.getInstance();
113 |
114 | if (userPhotoUri!=null) {
115 | userModel.setUserphoto( uid );
116 | }
117 |
118 | db.collection("users").document(uid)
119 | .set(userModel)
120 | .addOnSuccessListener(new OnSuccessListener() {
121 | @Override
122 | public void onSuccess(Void aVoid) {
123 | if (userPhotoUri==null) {
124 | Util9.showMessage(getActivity(), "Success to Save.");
125 | } else {
126 | // small image
127 | Glide.with(getContext())
128 | .asBitmap()
129 | .load(userPhotoUri)
130 | .apply(new RequestOptions().override(150, 150))
131 | .into(new SimpleTarget() {
132 | @Override
133 | public void onResourceReady(Bitmap bitmap, Transition super Bitmap> transition) {
134 | ByteArrayOutputStream baos = new ByteArrayOutputStream();
135 | bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
136 | byte[] data = baos.toByteArray();
137 | FirebaseStorage.getInstance().getReference().child("userPhoto/" + uid).putBytes(data);
138 | Util9.showMessage(getActivity(), "Success to Save.");
139 | }
140 | });
141 | }
142 | }
143 | });
144 | }
145 | };
146 |
147 | Button.OnClickListener changePWBtnClickListener = new View.OnClickListener() {
148 | public void onClick(final View view) {
149 | startActivity(new Intent(getActivity(), UserPWActivity.class));
150 | }
151 | };
152 |
153 | private boolean validateForm() {
154 | boolean valid = true;
155 |
156 | String userName = user_name.getText().toString();
157 | if (TextUtils.isEmpty(userName)) {
158 | user_name.setError("Required.");
159 | valid = false;
160 | } else {
161 | user_name.setError(null);
162 | }
163 |
164 | String userMsg = user_msg.getText().toString();
165 | if (TextUtils.isEmpty(userMsg)) {
166 | user_msg.setError("Required.");
167 | valid = false;
168 | } else {
169 | user_msg.setError(null);
170 | }
171 | Util9.hideKeyboard(getActivity());
172 |
173 | return valid;
174 | }
175 |
176 | }
177 |
--------------------------------------------------------------------------------
/app/src/main/java/gujc/directtalk9/chat/SelectUserActivity.java:
--------------------------------------------------------------------------------
1 | package gujc.directtalk9.chat;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.support.annotation.NonNull;
6 | import android.support.v7.app.AppCompatActivity;
7 | import android.support.v7.widget.LinearLayoutManager;
8 | import android.support.v7.widget.RecyclerView;
9 | import android.view.LayoutInflater;
10 | import android.view.View;
11 | import android.view.ViewGroup;
12 | import android.widget.Button;
13 | import android.widget.CheckBox;
14 | import android.widget.CompoundButton;
15 | import android.widget.ImageView;
16 | import android.widget.TextView;
17 |
18 | import com.bumptech.glide.Glide;
19 | import com.bumptech.glide.load.resource.bitmap.CenterCrop;
20 | import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
21 | import com.bumptech.glide.request.RequestOptions;
22 | import gujc.directtalk9.R;
23 | import gujc.directtalk9.common.FirestoreAdapter;
24 | import gujc.directtalk9.common.Util9;
25 | import gujc.directtalk9.model.UserModel;
26 |
27 | import com.google.android.gms.tasks.OnCompleteListener;
28 | import com.google.android.gms.tasks.Task;
29 | import com.google.firebase.auth.FirebaseAuth;
30 | import com.google.firebase.firestore.DocumentReference;
31 | import com.google.firebase.firestore.DocumentSnapshot;
32 | import com.google.firebase.firestore.FirebaseFirestore;
33 | import com.google.firebase.firestore.Query;
34 | import com.google.firebase.storage.FirebaseStorage;
35 | import com.google.firebase.storage.StorageReference;
36 |
37 | import java.util.HashMap;
38 | import java.util.Map;
39 |
40 | public class SelectUserActivity extends AppCompatActivity {
41 | private String roomID;
42 | private Map selectedUsers = new HashMap<>();
43 | private FirestoreAdapter firestoreAdapter;
44 |
45 | @Override
46 | public void onStart() {
47 | super.onStart();
48 | if (firestoreAdapter != null) {
49 | firestoreAdapter.startListening();
50 | }
51 | }
52 |
53 | @Override
54 | public void onStop() {
55 | super.onStop();
56 | if (firestoreAdapter != null) {
57 | firestoreAdapter.stopListening();
58 | }
59 | }
60 |
61 | @Override
62 | protected void onCreate(Bundle savedInstanceState) {
63 | super.onCreate(savedInstanceState);
64 | setContentView(R.layout.activity_select_user);
65 |
66 | roomID = getIntent().getStringExtra("roomID");
67 |
68 | firestoreAdapter = new RecyclerViewAdapter(FirebaseFirestore.getInstance().collection("users").orderBy("usernm"));
69 | RecyclerView recyclerView = findViewById(R.id.recyclerView);
70 | recyclerView.setLayoutManager( new LinearLayoutManager((this)));
71 | recyclerView.setAdapter(firestoreAdapter);
72 |
73 | Button makeRoomBtn = findViewById(R.id.makeRoomBtn);
74 | if (roomID==null)
75 | makeRoomBtn.setOnClickListener(makeRoomClickListener);
76 | else makeRoomBtn.setOnClickListener(addRoomUserClickListener);
77 | }
78 |
79 | Button.OnClickListener makeRoomClickListener = new View.OnClickListener() {
80 | public void onClick(View view) {
81 | if (selectedUsers.size() <2) {
82 | Util9.showMessage(getApplicationContext(), "Please select 2 or more user");
83 | return;
84 | }
85 |
86 | selectedUsers.put(FirebaseAuth.getInstance().getCurrentUser().getUid(), "");
87 |
88 | DocumentReference newRoom = FirebaseFirestore.getInstance().collection("rooms").document();
89 | CreateChattingRoom(newRoom);
90 | }
91 | };
92 |
93 | Button.OnClickListener addRoomUserClickListener = new View.OnClickListener() {
94 | public void onClick(View view) {
95 | if (selectedUsers.size() <1) {
96 | Util9.showMessage(getApplicationContext(), "Please select 1 or more user");
97 | return;
98 | }
99 | CreateChattingRoom(FirebaseFirestore.getInstance().collection("rooms").document(roomID) );
100 | }
101 | };
102 |
103 | public void CreateChattingRoom(final DocumentReference room) {
104 | String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
105 | Map users = new HashMap<>();
106 | String title = "";
107 | for( String key : selectedUsers.keySet()) {
108 | users.put(key, 0);
109 | if (title.length() < 20 & !key.equals(uid)) {
110 | title += selectedUsers.get(key) + ", ";
111 | }
112 | }
113 | Map data = new HashMap<>();
114 | data.put("title", title.substring(0, title.length() - 2));
115 | data.put("users", users);
116 |
117 | room.set(data).addOnCompleteListener(new OnCompleteListener() {
118 | @Override
119 | public void onComplete(@NonNull Task task) {
120 | if (task.isSuccessful()) {
121 | Intent intent = new Intent(SelectUserActivity.this, ChatActivity.class);
122 | intent.putExtra("roomID", room.getId());
123 | startActivity(intent);
124 | SelectUserActivity.this.finish();
125 | }
126 | }
127 | });
128 | }
129 |
130 | class RecyclerViewAdapter extends FirestoreAdapter {
131 |
132 | final private RequestOptions requestOptions = new RequestOptions().transforms(new CenterCrop(), new RoundedCorners(90));
133 | private StorageReference storageReference;
134 | private String myUid = FirebaseAuth.getInstance().getCurrentUser().getUid();
135 |
136 | RecyclerViewAdapter(Query query) {
137 | super(query);
138 | storageReference = FirebaseStorage.getInstance().getReference();
139 | }
140 |
141 | @NonNull
142 | @Override
143 | public CustomViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
144 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_select_user, parent, false);
145 | return new CustomViewHolder(view);
146 | }
147 |
148 | @Override
149 | public void onBindViewHolder(@NonNull CustomViewHolder viewHolder, int position) {
150 | DocumentSnapshot documentSnapshot = getSnapshot(position);
151 | final UserModel userModel = documentSnapshot.toObject(UserModel.class);
152 |
153 | if (myUid.equals(userModel.getUid())) {
154 | viewHolder.itemView.setVisibility(View.INVISIBLE);
155 | viewHolder.itemView.getLayoutParams().height = 0;
156 | return;
157 | }
158 |
159 | viewHolder.user_name.setText(userModel.getUsernm());
160 |
161 | if (userModel.getUserphoto()==null) {
162 | Glide.with(getApplicationContext()).load(R.drawable.user)
163 | .apply(requestOptions)
164 | .into(viewHolder.user_photo);
165 | } else{
166 | Glide.with(getApplicationContext())
167 | .load(storageReference.child("userPhoto/"+userModel.getUserphoto()))
168 | .apply(requestOptions)
169 | .into(viewHolder.user_photo);
170 | }
171 |
172 | viewHolder.userChk.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
173 | @Override
174 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
175 | if (isChecked) {
176 | selectedUsers.put(userModel.getUid(), userModel.getUsernm());
177 | } else {
178 | selectedUsers.remove(userModel.getUid());
179 | }
180 | }
181 | });
182 | }
183 | }
184 |
185 | private class CustomViewHolder extends RecyclerView.ViewHolder {
186 | public ImageView user_photo;
187 | public TextView user_name;
188 | public CheckBox userChk;
189 |
190 | CustomViewHolder(View view) {
191 | super(view);
192 | user_photo = view.findViewById(R.id.user_photo);
193 | user_name = view.findViewById(R.id.user_name);
194 | userChk = view.findViewById(R.id.userChk);
195 | }
196 | }
197 |
198 | }
199 |
--------------------------------------------------------------------------------
/app/src/main/java/gujc/directtalk9/fragment/ChatRoomFragment.java:
--------------------------------------------------------------------------------
1 | package gujc.directtalk9.fragment;
2 |
3 | import android.content.Context;
4 | import android.content.pm.PackageManager;
5 | import android.content.pm.ResolveInfo;
6 | import android.support.v4.app.Fragment;
7 | import android.content.Intent;
8 | import android.os.Bundle;
9 | import android.support.annotation.NonNull;
10 | import android.support.annotation.Nullable;
11 | import android.support.v7.widget.LinearLayoutManager;
12 | import android.support.v7.widget.RecyclerView;
13 | import android.view.LayoutInflater;
14 | import android.view.View;
15 | import android.view.ViewGroup;
16 | import android.widget.ImageView;
17 | import android.widget.TextView;
18 |
19 | import com.bumptech.glide.Glide;
20 | import com.bumptech.glide.load.resource.bitmap.CenterCrop;
21 | import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
22 | import com.bumptech.glide.request.RequestOptions;
23 | import gujc.directtalk9.R;
24 | import gujc.directtalk9.chat.ChatActivity;
25 | import gujc.directtalk9.model.ChatModel;
26 | import gujc.directtalk9.model.ChatRoomModel;
27 | import gujc.directtalk9.model.Message;
28 | import gujc.directtalk9.model.UserModel;
29 |
30 | import com.google.firebase.auth.FirebaseAuth;
31 | import com.google.firebase.firestore.EventListener;
32 | import com.google.firebase.firestore.FirebaseFirestore;
33 | import com.google.firebase.firestore.FirebaseFirestoreException;
34 | import com.google.firebase.firestore.ListenerRegistration;
35 | import com.google.firebase.firestore.QueryDocumentSnapshot;
36 | import com.google.firebase.firestore.QuerySnapshot;
37 | import com.google.firebase.storage.FirebaseStorage;
38 | import com.google.firebase.storage.StorageReference;
39 |
40 | import java.text.SimpleDateFormat;
41 | import java.util.ArrayList;
42 | import java.util.Collections;
43 | import java.util.Date;
44 | import java.util.HashMap;
45 | import java.util.List;
46 | import java.util.Map;
47 | import java.util.TimeZone;
48 | import java.util.TreeMap;
49 |
50 | public class ChatRoomFragment extends Fragment{
51 |
52 | private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
53 | private RecyclerViewAdapter mAdapter;
54 |
55 | public ChatRoomFragment() {
56 | }
57 |
58 | @Nullable
59 | @Override
60 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
61 | View view = inflater.inflate(R.layout.fragment_chatroom, container, false);
62 |
63 | RecyclerView recyclerView = view.findViewById(R.id.recyclerView);
64 | recyclerView.setLayoutManager(new LinearLayoutManager(inflater.getContext()));
65 | mAdapter = new RecyclerViewAdapter();
66 | recyclerView.setAdapter(mAdapter);
67 |
68 | simpleDateFormat.setTimeZone(TimeZone.getTimeZone("Asia/Seoul"));
69 |
70 | return view;
71 | }
72 |
73 | @Override
74 | public void onDestroyView() {
75 | super.onDestroyView();
76 | if (mAdapter != null) {
77 | mAdapter.stopListening();
78 | }
79 | }
80 | // =============================================================================================
81 | class RecyclerViewAdapter extends RecyclerView.Adapter{
82 | final private RequestOptions requestOptions = new RequestOptions().transforms(new CenterCrop(), new RoundedCorners(90));
83 | private List roomList = new ArrayList<>();
84 | private Map userList = new HashMap<>();
85 | private String myUid;
86 | private StorageReference storageReference;
87 | private FirebaseFirestore firestore;
88 | private ListenerRegistration listenerRegistration;
89 | private ListenerRegistration listenerUsers;
90 |
91 | RecyclerViewAdapter() {
92 | firestore = FirebaseFirestore.getInstance();
93 | storageReference = FirebaseStorage.getInstance().getReference();
94 | myUid = FirebaseAuth.getInstance().getCurrentUser().getUid();
95 |
96 | // all users information
97 | listenerUsers = firestore.collection("users")
98 | .addSnapshotListener(new EventListener() {
99 | @Override
100 | public void onEvent(@Nullable QuerySnapshot value,
101 | @Nullable FirebaseFirestoreException e) {
102 | if (e != null) {return;}
103 |
104 | for (QueryDocumentSnapshot doc : value) {
105 | userList.put(doc.getId(), doc.toObject(UserModel.class));
106 | }
107 | getRoomInfo();
108 | }
109 | });
110 | }
111 |
112 | Integer unreadTotal = 0;
113 | public void getRoomInfo() {
114 | // my chatting room information
115 | listenerRegistration = firestore.collection("rooms").whereGreaterThanOrEqualTo("users."+myUid, 0)
116 | // a.orderBy("timestamp", Query.Direction.DESCENDING)
117 | .addSnapshotListener(new EventListener() {
118 | @Override
119 | public void onEvent(@Nullable QuerySnapshot value,
120 | @Nullable FirebaseFirestoreException e) {
121 | if (e != null) {return;}
122 |
123 | TreeMap orderedRooms = new TreeMap(Collections.reverseOrder());
124 |
125 | for (final QueryDocumentSnapshot document : value) {
126 | Message message = document.toObject(Message.class);
127 | if (message.getMsg() !=null & message.getTimestamp() == null) {continue;} // FieldValue.serverTimestamp is so late
128 |
129 | ChatRoomModel chatRoomModel = new ChatRoomModel();
130 | chatRoomModel.setRoomID(document.getId());
131 |
132 | if (message.getMsg() !=null) { // there are no last message
133 | chatRoomModel.setLastDatetime(simpleDateFormat.format(message.getTimestamp()));
134 | switch(message.getMsgtype()){
135 | case "1": chatRoomModel.setLastMsg("Image"); break;
136 | case "2": chatRoomModel.setLastMsg("File"); break;
137 | default: chatRoomModel.setLastMsg(message.getMsg());
138 | }
139 | }
140 | Map users = (Map) document.get("users");
141 | chatRoomModel.setUserCount(users.size());
142 | for( String key : users.keySet() ){
143 | if (myUid.equals(key)) {
144 | Integer unread = (int) (long) users.get(key);
145 | unreadTotal += unread;
146 | chatRoomModel.setUnreadCount(unread);
147 | break;
148 | }
149 | }
150 | if (users.size()==2) {
151 | for( String key : users.keySet() ){
152 | if (myUid.equals(key)) continue;
153 | UserModel userModel = userList.get(key);
154 | chatRoomModel.setTitle(userModel.getUsernm());
155 | chatRoomModel.setPhoto(userModel.getUserphoto());
156 | }
157 | } else { // group chat room
158 | chatRoomModel.setTitle(document.getString("title"));
159 | }
160 | if (message.getTimestamp()==null) message.setTimestamp(new Date());
161 | orderedRooms.put(message.getTimestamp(), chatRoomModel);
162 | }
163 | roomList.clear();
164 | for(Map.Entry entry : orderedRooms.entrySet()) {
165 | roomList.add(entry.getValue());
166 | }
167 | notifyDataSetChanged();
168 | setBadge(getContext(), unreadTotal);
169 | }
170 | });
171 | }
172 |
173 | public void stopListening() {
174 | if (listenerRegistration != null) {
175 | listenerRegistration.remove();
176 | listenerRegistration = null;
177 | }
178 | if (listenerUsers != null) {
179 | listenerUsers.remove();
180 | listenerUsers = null;
181 | }
182 |
183 | roomList.clear();
184 | notifyDataSetChanged();
185 | }
186 |
187 | @NonNull
188 | @Override
189 | public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
190 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_chatroom, parent, false);
191 | return new RoomViewHolder(view);
192 | }
193 |
194 | @Override
195 | public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
196 | RoomViewHolder roomViewHolder = (RoomViewHolder) holder;
197 |
198 | final ChatRoomModel chatRoomModel = roomList.get(position);
199 |
200 | roomViewHolder.room_title.setText(chatRoomModel.getTitle());
201 | roomViewHolder.last_msg.setText(chatRoomModel.getLastMsg());
202 | roomViewHolder.last_time.setText(chatRoomModel.getLastDatetime());
203 |
204 | if (chatRoomModel.getPhoto()==null) {
205 | Glide.with(getActivity()).load(R.drawable.user)
206 | .apply(requestOptions)
207 | .into(roomViewHolder.room_image);
208 | } else{
209 | Glide.with(getActivity()).load(storageReference.child("userPhoto/"+chatRoomModel.getPhoto()))
210 | .apply(requestOptions)
211 | .into(roomViewHolder.room_image);
212 | }
213 | if (chatRoomModel.getUserCount() > 2) {
214 | roomViewHolder.room_count.setText(chatRoomModel.getUserCount().toString());
215 | roomViewHolder.room_count.setVisibility(View.VISIBLE);
216 | } else {
217 | roomViewHolder.room_count.setVisibility(View.INVISIBLE);
218 | }
219 | if (chatRoomModel.getUnreadCount() > 0) {
220 | roomViewHolder.unread_count.setText(chatRoomModel.getUnreadCount().toString());
221 | roomViewHolder.unread_count.setVisibility(View.VISIBLE);
222 | } else {
223 | roomViewHolder.unread_count.setVisibility(View.INVISIBLE);
224 | }
225 |
226 | roomViewHolder.itemView.setOnClickListener(new View.OnClickListener(){
227 | @Override
228 | public void onClick(View v) {
229 | Intent intent = new Intent(v.getContext(), ChatActivity.class);
230 | intent.putExtra("roomID", chatRoomModel.getRoomID());
231 | intent.putExtra("roomTitle", chatRoomModel.getTitle());
232 | startActivity(intent);
233 | }
234 | });
235 | }
236 |
237 | @Override
238 | public int getItemCount() {
239 | return roomList.size();
240 | }
241 |
242 | private class RoomViewHolder extends RecyclerView.ViewHolder {
243 | public ImageView room_image;
244 | public TextView room_title;
245 | public TextView last_msg;
246 | public TextView last_time;
247 | public TextView room_count;
248 | public TextView unread_count;
249 |
250 | RoomViewHolder(View view) {
251 | super(view);
252 | room_image = view.findViewById(R.id.room_image);
253 | room_title = view.findViewById(R.id.room_title);
254 | last_msg = view.findViewById(R.id.last_msg);
255 | last_time = view.findViewById(R.id.last_time);
256 | room_count = view.findViewById(R.id.room_count);
257 | unread_count = view.findViewById(R.id.unread_count);
258 | }
259 | }
260 | }
261 |
262 | public static void setBadge(Context context, int count) {
263 | String launcherClassName = getLauncherClassName(context);
264 | if (launcherClassName == null) {
265 | return;
266 | }
267 | Intent intent = new Intent("android.intent.action.BADGE_COUNT_UPDATE");
268 | intent.putExtra("badge_count", count);
269 | intent.putExtra("badge_count_package_name", context.getPackageName());
270 | intent.putExtra("badge_count_class_name", launcherClassName);
271 | context.sendBroadcast(intent);
272 | }
273 |
274 | public static String getLauncherClassName(Context context) {
275 |
276 | PackageManager pm = context.getPackageManager();
277 |
278 | Intent intent = new Intent(Intent.ACTION_MAIN);
279 | intent.addCategory(Intent.CATEGORY_LAUNCHER);
280 |
281 | List resolveInfos = pm.queryIntentActivities(intent, 0);
282 | for (ResolveInfo resolveInfo : resolveInfos) {
283 | String pkgName = resolveInfo.activityInfo.applicationInfo.packageName;
284 | if (pkgName.equalsIgnoreCase(context.getPackageName())) {
285 | return resolveInfo.activityInfo.name;
286 | }
287 | }
288 | return null;
289 | }
290 | }
291 |
--------------------------------------------------------------------------------
/app/src/main/java/gujc/directtalk9/fragment/ChatFragment.java:
--------------------------------------------------------------------------------
1 | package gujc.directtalk9.fragment;
2 |
3 | import android.Manifest;
4 | import android.app.ProgressDialog;
5 | import android.content.ContentResolver;
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.content.pm.PackageManager;
9 | import android.content.pm.ResolveInfo;
10 | import android.database.Cursor;
11 | import android.graphics.Bitmap;
12 | import android.net.Uri;
13 | import android.os.Build;
14 | import android.os.Bundle;
15 | import android.provider.MediaStore;
16 | import android.provider.OpenableColumns;
17 | import android.support.annotation.NonNull;
18 | import android.support.annotation.Nullable;
19 | import android.support.v4.app.Fragment;
20 | import android.support.v4.content.FileProvider;
21 | import android.support.v7.widget.LinearLayoutManager;
22 | import android.support.v7.widget.RecyclerView;
23 | import android.util.Log;
24 | import android.view.LayoutInflater;
25 | import android.view.View;
26 | import android.view.ViewGroup;
27 | import android.webkit.MimeTypeMap;
28 | import android.widget.Button;
29 | import android.widget.EditText;
30 | import android.widget.ImageView;
31 | import android.widget.LinearLayout;
32 | import android.widget.TextView;
33 |
34 | import com.bumptech.glide.Glide;
35 | import com.bumptech.glide.load.resource.bitmap.CenterCrop;
36 | import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
37 | import com.bumptech.glide.request.RequestOptions;
38 | import com.bumptech.glide.request.target.SimpleTarget;
39 | import com.bumptech.glide.request.transition.Transition;
40 | import com.google.android.gms.tasks.OnCompleteListener;
41 | import com.google.android.gms.tasks.OnFailureListener;
42 | import com.google.android.gms.tasks.OnSuccessListener;
43 | import com.google.android.gms.tasks.Task;
44 | import com.google.firebase.auth.FirebaseAuth;
45 | import com.google.firebase.firestore.CollectionReference;
46 | import com.google.firebase.firestore.DocumentChange;
47 | import com.google.firebase.firestore.DocumentReference;
48 | import com.google.firebase.firestore.DocumentSnapshot;
49 | import com.google.firebase.firestore.EventListener;
50 | import com.google.firebase.firestore.FieldValue;
51 | import com.google.firebase.firestore.FirebaseFirestore;
52 | import com.google.firebase.firestore.FirebaseFirestoreException;
53 | import com.google.firebase.firestore.ListenerRegistration;
54 | import com.google.firebase.firestore.QueryDocumentSnapshot;
55 | import com.google.firebase.firestore.QuerySnapshot;
56 | import com.google.firebase.firestore.SetOptions;
57 | import com.google.firebase.firestore.WriteBatch;
58 | import com.google.firebase.storage.FileDownloadTask;
59 | import com.google.firebase.storage.FirebaseStorage;
60 | import com.google.firebase.storage.StorageReference;
61 | import com.google.firebase.storage.UploadTask;
62 | import com.google.gson.Gson;
63 |
64 | import java.io.ByteArrayOutputStream;
65 | import java.io.File;
66 | import java.io.IOException;
67 | import java.text.SimpleDateFormat;
68 | import java.util.ArrayList;
69 | import java.util.Date;
70 | import java.util.HashMap;
71 | import java.util.List;
72 | import java.util.Map;
73 | import java.util.TimeZone;
74 |
75 | import gujc.directtalk9.R;
76 | import gujc.directtalk9.common.Util9;
77 | import gujc.directtalk9.model.ChatModel;
78 | import gujc.directtalk9.model.Message;
79 | import gujc.directtalk9.model.NotificationModel;
80 | import gujc.directtalk9.model.UserModel;
81 | import gujc.directtalk9.photoview.ViewPagerActivity;
82 | import okhttp3.Call;
83 | import okhttp3.Callback;
84 | import okhttp3.MediaType;
85 | import okhttp3.OkHttpClient;
86 | import okhttp3.Request;
87 | import okhttp3.RequestBody;
88 | import okhttp3.Response;
89 |
90 | import static android.app.Activity.RESULT_OK;
91 |
92 | public class ChatFragment extends Fragment{
93 |
94 | private static final int PICK_FROM_ALBUM = 1;
95 | private static final int PICK_FROM_FILE = 2;
96 | private static String rootPath = Util9.getRootPath()+"/DirectTalk9/";
97 |
98 | private Button sendBtn;
99 | private EditText msg_input;
100 | private RecyclerView recyclerView;
101 | private RecyclerViewAdapter mAdapter;
102 | private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
103 | private SimpleDateFormat dateFormatDay = new SimpleDateFormat("yyyy-MM-dd");
104 | private SimpleDateFormat dateFormatHour = new SimpleDateFormat("aa hh:mm");
105 | private String roomID;
106 | private String myUid;
107 | private String toUid;
108 | private Map userList = new HashMap<>();
109 |
110 | private ListenerRegistration listenerRegistration;
111 | private FirebaseFirestore firestore=null;
112 | private StorageReference storageReference;
113 | private LinearLayoutManager linearLayoutManager;
114 |
115 | private ProgressDialog progressDialog = null;
116 | private Integer userCount = 0;
117 |
118 | public ChatFragment() {
119 | }
120 |
121 | public static final ChatFragment getInstance(String toUid, String roomID) {
122 | ChatFragment f = new ChatFragment();
123 | Bundle bdl = new Bundle();
124 | bdl.putString("toUid", toUid);
125 | bdl.putString("roomID", roomID);
126 | f.setArguments(bdl);
127 | return f;
128 | }
129 |
130 | @Nullable
131 | @Override
132 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
133 | View view = inflater.inflate(R.layout.fragment_chat, container, false);
134 |
135 | recyclerView = view.findViewById(R.id.recyclerView);
136 | linearLayoutManager = new LinearLayoutManager(getContext());
137 | recyclerView.setLayoutManager(linearLayoutManager);
138 |
139 | msg_input = view.findViewById(R.id.msg_input);
140 | sendBtn = view.findViewById(R.id.sendBtn);
141 | sendBtn.setOnClickListener(sendBtnClickListener);
142 |
143 | view.findViewById(R.id.imageBtn).setOnClickListener(imageBtnClickListener);
144 | view.findViewById(R.id.fileBtn).setOnClickListener(fileBtnClickListener);
145 | view.findViewById(R.id.msg_input).setOnFocusChangeListener(new View.OnFocusChangeListener() {
146 | @Override
147 | public void onFocusChange(View v, boolean hasFocus) {
148 | if(!hasFocus) {
149 | Util9.hideKeyboard(getActivity());
150 | }
151 | }
152 | });
153 |
154 | if (getArguments() != null) {
155 | roomID = getArguments().getString("roomID");
156 | toUid = getArguments().getString("toUid");
157 | }
158 |
159 | firestore = FirebaseFirestore.getInstance();
160 | storageReference = FirebaseStorage.getInstance().getReference();
161 |
162 | dateFormatDay.setTimeZone(TimeZone.getTimeZone("Asia/Seoul"));
163 | dateFormatHour.setTimeZone(TimeZone.getTimeZone("Asia/Seoul"));
164 |
165 | myUid = FirebaseAuth.getInstance().getCurrentUser().getUid();
166 |
167 | /*
168 | two user: roomid or uid talking
169 | multi user: roomid
170 | */
171 | if (!"".equals(toUid) && toUid!=null) { // find existing room for two user
172 | findChatRoom(toUid);
173 | } else
174 | if (!"".equals(roomID) && roomID!=null) { // existing room (multi user)
175 | setChatRoom(roomID);
176 | };
177 |
178 | if (roomID==null) { // new room for two user
179 | getUserInfoFromServer(myUid);
180 | getUserInfoFromServer(toUid);
181 | userCount = 2;
182 | };
183 |
184 | recyclerView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
185 | @Override
186 | public void onLayoutChange(View v,
187 | int left, int top, int right, int bottom,
188 | int oldLeft, int oldTop, int oldRight, int oldBottom) {
189 | if (mAdapter!=null & bottom < oldBottom) {
190 | final int lastAdapterItem = mAdapter.getItemCount() - 1;
191 | recyclerView.post(new Runnable() {
192 | @Override
193 | public void run() {
194 | int recyclerViewPositionOffset = -1000000;
195 | View bottomView = linearLayoutManager.findViewByPosition(lastAdapterItem);
196 | if (bottomView != null) {
197 | recyclerViewPositionOffset = 0 - bottomView.getHeight();
198 | }
199 | linearLayoutManager.scrollToPositionWithOffset(lastAdapterItem, recyclerViewPositionOffset);
200 | }
201 | });
202 | }
203 | }
204 | });
205 |
206 | return view;
207 | }
208 |
209 | @Override
210 | public void onDestroyView() {
211 | super.onDestroyView();
212 | if (mAdapter != null) {
213 | mAdapter.stopListening();
214 | }
215 | }
216 |
217 | // get a user info
218 | void getUserInfoFromServer(String id){
219 | firestore.collection("users").document(id).get().addOnSuccessListener(new OnSuccessListener() {
220 | @Override
221 | public void onSuccess(DocumentSnapshot documentSnapshot) {
222 | UserModel userModel = documentSnapshot.toObject(UserModel.class);
223 | userList.put(userModel.getUid(), userModel);
224 | if (roomID != null & userCount == userList.size()) {
225 | mAdapter = new RecyclerViewAdapter();
226 | recyclerView.setAdapter(mAdapter);
227 | }
228 | }
229 | });
230 | }
231 |
232 | // Returns the room ID after locating the chatting room with the user ID.
233 | void findChatRoom(final String toUid){
234 | firestore.collection("rooms").whereGreaterThanOrEqualTo("users."+myUid, 0).get()
235 | .addOnCompleteListener(new OnCompleteListener() {
236 | @Override
237 | public void onComplete(@NonNull Task task) {
238 | if (!task.isSuccessful()) {return;}
239 |
240 | for (QueryDocumentSnapshot document : task.getResult()) {
241 | Map users = (Map) document.get("users");
242 | if (users.size()==2 & users.get(toUid)!=null){
243 | setChatRoom(document.getId());
244 | break;
245 | }
246 | }
247 | }
248 | });
249 | }
250 |
251 | // get user list in a chatting room
252 | void setChatRoom(String rid) {
253 | roomID = rid;
254 | firestore.collection("rooms").document(roomID).get().addOnCompleteListener(new OnCompleteListener() {
255 | @Override
256 | public void onComplete(@NonNull Task task) {
257 | if (!task.isSuccessful()) {return;}
258 | DocumentSnapshot document = task.getResult();
259 | Map users = (Map) document.get("users");
260 |
261 | for( String key : users.keySet() ){
262 | getUserInfoFromServer(key);
263 | }
264 | userCount = users.size();
265 | //users.put(myUid, (long) 0);
266 | //document.getReference().update("users", users);
267 | }
268 | });
269 | }
270 |
271 | void setUnread2Read() {
272 | if (roomID==null) return;
273 |
274 | firestore.collection("rooms").document(roomID).get().addOnCompleteListener(new OnCompleteListener() {
275 | @Override
276 | public void onComplete(@NonNull Task task) {
277 | if (!task.isSuccessful()) {return;}
278 | DocumentSnapshot document = task.getResult();
279 | Map users = (Map) document.get("users");
280 |
281 | users.put(myUid, (long) 0);
282 | document.getReference().update("users", users);
283 | }
284 | });
285 | }
286 |
287 | public void CreateChattingRoom(final DocumentReference room) {
288 | Map users = new HashMap<>();
289 | String title = "";
290 | for( String key : userList.keySet() ){
291 | users.put(key, 0);
292 | }
293 | Map data = new HashMap<>();
294 | data.put("title", null);
295 | data.put("users", users);
296 |
297 | room.set(data).addOnCompleteListener(new OnCompleteListener() {
298 | @Override
299 | public void onComplete(@NonNull Task task) {
300 | if (task.isSuccessful()) {
301 | mAdapter = new RecyclerViewAdapter();
302 | recyclerView.setAdapter(mAdapter);
303 | }
304 | }
305 | });
306 | }
307 | public Map getUserList() {
308 | return userList;
309 | }
310 |
311 | Button.OnClickListener sendBtnClickListener = new View.OnClickListener() {
312 | public void onClick(View view) {
313 | String msg = msg_input.getText().toString();
314 | sendMessage(msg, "0", null);
315 | msg_input.setText("");
316 | }
317 | };
318 |
319 | private void sendMessage(final String msg, String msgtype, final ChatModel.FileInfo fileinfo) {
320 | sendBtn.setEnabled(false);
321 |
322 | if (roomID==null) { // create chatting room for two user
323 | roomID = firestore.collection("rooms").document().getId();
324 | CreateChattingRoom( firestore.collection("rooms").document(roomID) );
325 | }
326 |
327 | final Map messages = new HashMap<>();
328 | messages.put("uid", myUid);
329 | messages.put("msg", msg);
330 | messages.put("msgtype", msgtype);
331 | messages.put("timestamp", FieldValue.serverTimestamp());
332 | if (fileinfo!=null){
333 | messages.put("filename", fileinfo.filename);
334 | messages.put("filesize", fileinfo.filesize);
335 | }
336 |
337 | final DocumentReference docRef = firestore.collection("rooms").document(roomID);
338 | docRef.get().addOnCompleteListener(new OnCompleteListener() {
339 | @Override
340 | public void onComplete(@NonNull Task task) {
341 | if (!task.isSuccessful()) {return;}
342 |
343 | WriteBatch batch = firestore.batch();
344 |
345 | // save last message
346 | batch.set(docRef, messages, SetOptions.merge());
347 |
348 | // save message
349 | List readUsers = new ArrayList();
350 | readUsers.add(myUid);
351 | messages.put("readUsers", readUsers);//new String[]{myUid} );
352 | batch.set(docRef.collection("messages").document(), messages);
353 |
354 | // inc unread message count
355 | DocumentSnapshot document = task.getResult();
356 | Map users = (Map) document.get("users");
357 |
358 | for( String key : users.keySet() ){
359 | if (!myUid.equals(key)) users.put(key, users.get(key)+1);
360 | }
361 | document.getReference().update("users", users);
362 |
363 | batch.commit().addOnCompleteListener(new OnCompleteListener() {
364 | @Override
365 | public void onComplete(@NonNull Task task) {
366 | if (task.isSuccessful()) {
367 | //sendGCM();
368 | sendBtn.setEnabled(true);
369 | }
370 | }
371 | });
372 | }
373 |
374 | });
375 | };
376 |
377 | void sendGCM(){
378 | Gson gson = new Gson();
379 | NotificationModel notificationModel = new NotificationModel();
380 | notificationModel.notification.title = userList.get(myUid).getUsernm();
381 | notificationModel.notification.body = msg_input.getText().toString();
382 | notificationModel.data.title = userList.get(myUid).getUsernm();
383 | notificationModel.data.body = msg_input.getText().toString();
384 |
385 | for ( Map.Entry elem : userList.entrySet() ){
386 | if (myUid.equals(elem.getValue().getUid())) continue;
387 | notificationModel.to = elem.getValue().getToken();
388 | RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf8"), gson.toJson(notificationModel));
389 | Request request = new Request.Builder()
390 | .header("Content-Type", "application/json")
391 | .addHeader("Authorization", "key=")
392 | .url("https://fcm.googleapis.com/fcm/send")
393 | .post(requestBody)
394 | .build();
395 |
396 | OkHttpClient okHttpClient = new OkHttpClient();
397 | okHttpClient.newCall(request).enqueue(new Callback() {
398 | @Override
399 | public void onFailure(Call call, IOException e) { }
400 | @Override
401 | public void onResponse(Call call, Response response) throws IOException { }
402 | });
403 | }
404 | }
405 |
406 | // choose image
407 | Button.OnClickListener imageBtnClickListener = new View.OnClickListener() {
408 | public void onClick(final View view) {
409 | Intent intent = new Intent(Intent.ACTION_PICK);
410 | intent.setType(MediaStore.Images.Media.CONTENT_TYPE);
411 | startActivityForResult(intent, PICK_FROM_ALBUM);
412 | }
413 | };
414 |
415 | // choose file
416 | Button.OnClickListener fileBtnClickListener = new View.OnClickListener() {
417 | public void onClick(final View view) {
418 | Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
419 | intent.setType("*/*");
420 | intent.setAction(Intent.ACTION_GET_CONTENT);
421 | startActivityForResult(Intent.createChooser(intent, "Select File"), PICK_FROM_FILE);
422 | }
423 | };
424 |
425 | // uploading image / file
426 | @Override
427 | public void onActivityResult(final int requestCode, int resultCode, Intent data) {
428 | if (resultCode!= RESULT_OK) { return;}
429 | Uri fileUri = data.getData();
430 | final String filename = Util9.getUniqueValue();
431 |
432 | showProgressDialog("Uploading selected File.");
433 | final ChatModel.FileInfo fileinfo = getFileDetailFromUri(getContext(), fileUri);
434 |
435 | storageReference.child("files/"+filename).putFile(fileUri).addOnCompleteListener(new OnCompleteListener() {
436 | @Override
437 | public void onComplete(@NonNull Task task) {
438 | sendMessage(filename, Integer.toString(requestCode), fileinfo);
439 | hideProgressDialog();
440 | }
441 | });
442 | if (requestCode != PICK_FROM_ALBUM) { return;}
443 |
444 | // small image
445 | Glide.with(getContext())
446 | .asBitmap()
447 | .load(fileUri)
448 | .apply(new RequestOptions().override(150, 150))
449 | .into(new SimpleTarget() {
450 | @Override
451 | public void onResourceReady(Bitmap bitmap, Transition super Bitmap> transition) {
452 | ByteArrayOutputStream baos = new ByteArrayOutputStream();
453 | bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
454 | byte[] data = baos.toByteArray();
455 | storageReference.child("filesmall/"+filename).putBytes(data);
456 | }
457 | });
458 | }
459 |
460 | // get file name and size from Uri
461 | public static ChatModel.FileInfo getFileDetailFromUri(final Context context, final Uri uri) {
462 | if (uri == null) { return null; }
463 |
464 | ChatModel.FileInfo fileDetail = new ChatModel.FileInfo();
465 | // File Scheme.
466 | if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
467 | File file = new File(uri.getPath());
468 | fileDetail.filename = file.getName();
469 | fileDetail.filesize = Util9.size2String(file.length());
470 | }
471 | // Content Scheme.
472 | else if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
473 | Cursor returnCursor =
474 | context.getContentResolver().query(uri, null, null, null, null);
475 | if (returnCursor != null && returnCursor.moveToFirst()) {
476 | int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
477 | int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
478 | fileDetail.filename = returnCursor.getString(nameIndex);
479 | fileDetail.filesize = Util9.size2String(returnCursor.getLong(sizeIndex));
480 | returnCursor.close();
481 | }
482 | }
483 |
484 | return fileDetail;
485 | }
486 |
487 | public void showProgressDialog(String title ) {
488 | if (progressDialog==null) {
489 | progressDialog = new ProgressDialog(getContext());
490 | }
491 | progressDialog.setIndeterminate(true);
492 | progressDialog.setTitle(title);
493 | progressDialog.setMessage("Please wait..");
494 | progressDialog.setCancelable(false);
495 | progressDialog.show();
496 | }
497 | public void setProgressDialog(int value) {
498 | progressDialog.setProgress(value);
499 | }
500 | public void hideProgressDialog() {
501 | progressDialog.dismiss();
502 | }
503 | // =======================================================================================
504 |
505 | class RecyclerViewAdapter extends RecyclerView.Adapter{
506 | final private RequestOptions requestOptions = new RequestOptions().transforms(new CenterCrop(), new RoundedCorners(90));
507 |
508 | List messageList;
509 | String beforeDay = null;
510 | MessageViewHolder beforeViewHolder;
511 |
512 | RecyclerViewAdapter() {
513 | File dir = new File(rootPath);
514 | if (!dir.exists()) {
515 | if (!Util9.isPermissionGranted(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
516 | return;
517 | }
518 | dir.mkdirs();
519 | }
520 |
521 | messageList = new ArrayList();
522 | setUnread2Read();
523 | startListening();
524 | }
525 |
526 | public void startListening() {
527 | beforeDay = null;
528 | messageList.clear();
529 |
530 | CollectionReference roomRef = firestore.collection("rooms").document(roomID).collection("messages");
531 | // my chatting room information
532 | listenerRegistration = roomRef.orderBy("timestamp").addSnapshotListener(new EventListener() {
533 | @Override
534 | public void onEvent(@Nullable QuerySnapshot documentSnapshots, @Nullable FirebaseFirestoreException e) {
535 | if (e != null) {return;}
536 |
537 | Message message;
538 | for (DocumentChange change : documentSnapshots.getDocumentChanges()) {
539 | switch (change.getType()) {
540 | case ADDED:
541 | message = change.getDocument().toObject(Message.class);
542 | //if (message.msg !=null & message.timestamp == null) {continue;} // FieldValue.serverTimestamp is so late
543 |
544 | if (message.getReadUsers().indexOf(myUid) == -1) {
545 | message.getReadUsers().add(myUid);
546 | change.getDocument().getReference().update("readUsers", message.getReadUsers());
547 | }
548 | messageList.add(message);
549 | notifyItemInserted(change.getNewIndex());
550 | break;
551 | case MODIFIED:
552 | message = change.getDocument().toObject(Message.class);
553 | messageList.set(change.getOldIndex(), message);
554 | notifyItemChanged(change.getOldIndex());
555 | break;
556 | case REMOVED:
557 | messageList.remove(change.getOldIndex());
558 | notifyItemRemoved(change.getOldIndex());
559 | break;
560 | }
561 | }
562 | recyclerView.scrollToPosition(messageList.size() - 1);
563 | }
564 | });
565 | }
566 |
567 | public void stopListening() {
568 | if (listenerRegistration != null) {
569 | listenerRegistration.remove();
570 | listenerRegistration = null;
571 | }
572 |
573 | messageList.clear();
574 | notifyDataSetChanged();
575 | }
576 |
577 | @Override
578 | public int getItemViewType(int position) {
579 | Message message = messageList.get(position);
580 | if (myUid.equals(message.getUid()) ) {
581 | switch(message.getMsgtype()){
582 | case "1": return R.layout.item_chatimage_right;
583 | case "2": return R.layout.item_chatfile_right;
584 | default: return R.layout.item_chatmsg_right;
585 | }
586 | } else {
587 | switch(message.getMsgtype()){
588 | case "1": return R.layout.item_chatimage_left;
589 | case "2": return R.layout.item_chatfile_left;
590 | default: return R.layout.item_chatmsg_left;
591 | }
592 | }
593 | }
594 | @NonNull
595 | @Override
596 | public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
597 | View view = null;
598 | view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
599 | return new MessageViewHolder(view);
600 | }
601 |
602 | @Override
603 | public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
604 | final MessageViewHolder messageViewHolder = (MessageViewHolder) holder;
605 | final Message message = messageList.get(position);
606 |
607 | setReadCounter(message, messageViewHolder.read_counter);
608 |
609 | if ("0".equals(message.getMsgtype())) { // text message
610 | messageViewHolder.msg_item.setText(message.getMsg());
611 | } else
612 | if ("2".equals(message.getMsgtype())) { // file transfer
613 | messageViewHolder.msg_item.setText(message.getFilename() + "\n" + message.getFilesize());
614 | messageViewHolder.filename = message.getFilename();
615 | messageViewHolder.realname = message.getMsg();
616 | File file = new File(rootPath + message.getFilename());
617 | if(file.exists()) {
618 | messageViewHolder.button_item.setText("Open File");
619 | } else {
620 | messageViewHolder.button_item.setText("Download");
621 | }
622 | } else { // image transfer
623 | messageViewHolder.realname = message.getMsg();
624 | Glide.with(getContext())
625 | .load(storageReference.child("filesmall/"+message.getMsg()))
626 | .apply(new RequestOptions().override(1000, 1000))
627 | .into(messageViewHolder.img_item);
628 | }
629 |
630 | if (! myUid.equals(message.getUid())) {
631 | UserModel userModel = userList.get(message.getUid());
632 | messageViewHolder.msg_name.setText(userModel.getUsernm());
633 |
634 | if (userModel.getUserphoto()==null) {
635 | Glide.with(getContext()).load(R.drawable.user)
636 | .apply(requestOptions)
637 | .into(messageViewHolder.user_photo);
638 | } else{
639 | Glide.with(getContext())
640 | .load(storageReference.child("userPhoto/"+userModel.getUserphoto()))
641 | .apply(requestOptions)
642 | .into(messageViewHolder.user_photo);
643 | }
644 | }
645 | messageViewHolder.divider.setVisibility(View.INVISIBLE);
646 | messageViewHolder.divider.getLayoutParams().height = 0;
647 | messageViewHolder.timestamp.setText("");
648 | if (message.getTimestamp()==null) {return;}
649 |
650 | String day = dateFormatDay.format( message.getTimestamp());
651 | String timestamp = dateFormatHour.format( message.getTimestamp());
652 | messageViewHolder.timestamp.setText(timestamp);
653 |
654 | if (position==0) {
655 | messageViewHolder.divider_date.setText(day);
656 | messageViewHolder.divider.setVisibility(View.VISIBLE);
657 | messageViewHolder.divider.getLayoutParams().height = 60;
658 | } else {
659 | Message beforeMsg = messageList.get(position - 1);
660 | String beforeDay = dateFormatDay.format( beforeMsg.getTimestamp() );
661 |
662 | if (!day.equals(beforeDay) && beforeDay != null) {
663 | messageViewHolder.divider_date.setText(day);
664 | messageViewHolder.divider.setVisibility(View.VISIBLE);
665 | messageViewHolder.divider.getLayoutParams().height = 60;
666 | }
667 | }
668 | /*messageViewHolder.timestamp.setText("");
669 | if (message.getTimestamp()==null) {return;}
670 |
671 | String day = dateFormatDay.format( message.getTimestamp());
672 | String timestamp = dateFormatHour.format( message.getTimestamp());
673 |
674 | messageViewHolder.timestamp.setText(timestamp);
675 |
676 | if (position==0) {
677 | messageViewHolder.divider_date.setText(day);
678 | messageViewHolder.divider.setVisibility(View.VISIBLE);
679 | messageViewHolder.divider.getLayoutParams().height = 60;
680 | };
681 | if (!day.equals(beforeDay) && beforeDay!=null) {
682 | beforeViewHolder.divider_date.setText(beforeDay);
683 | beforeViewHolder.divider.setVisibility(View.VISIBLE);
684 | beforeViewHolder.divider.getLayoutParams().height = 60;
685 | }
686 | beforeViewHolder = messageViewHolder;
687 | beforeDay = day;*/
688 | }
689 |
690 | void setReadCounter (Message message, final TextView textView) {
691 | int cnt = userCount - message.getReadUsers().size();
692 | if (cnt > 0) {
693 | textView.setVisibility(View.VISIBLE);
694 | textView.setText(String.valueOf(cnt));
695 | } else {
696 | textView.setVisibility(View.INVISIBLE);
697 | }
698 | }
699 |
700 | @Override
701 | public int getItemCount() {
702 | return messageList.size();
703 | }
704 |
705 |
706 | }
707 |
708 | private class MessageViewHolder extends RecyclerView.ViewHolder {
709 | public ImageView user_photo;
710 | public TextView msg_item;
711 | public ImageView img_item; // only item_chatimage_
712 | public TextView msg_name;
713 | public TextView timestamp;
714 | public TextView read_counter;
715 | public LinearLayout divider;
716 | public TextView divider_date;
717 | public TextView button_item; // only item_chatfile_
718 | public LinearLayout msgLine_item; // only item_chatfile_
719 | public String filename;
720 | public String realname;
721 |
722 | public MessageViewHolder(View view) {
723 | super(view);
724 | user_photo = view.findViewById(R.id.user_photo);
725 | msg_item = view.findViewById(R.id.msg_item);
726 | img_item = view.findViewById(R.id.img_item);
727 | timestamp = view.findViewById(R.id.timestamp);
728 | msg_name = view.findViewById(R.id.msg_name);
729 | read_counter = view.findViewById(R.id.read_counter);
730 | divider = view.findViewById(R.id.divider);
731 | divider_date = view.findViewById(R.id.divider_date);
732 | button_item = view.findViewById(R.id.button_item);
733 | msgLine_item = view.findViewById(R.id.msgLine_item); // for file
734 | if (msgLine_item!=null) {
735 | msgLine_item.setOnClickListener(downloadClickListener);
736 | }
737 | if (img_item!=null) { // for image
738 | img_item.setOnClickListener(imageClickListener);
739 | }
740 | }
741 | // file download and open
742 | Button.OnClickListener downloadClickListener = new View.OnClickListener() {
743 | public void onClick(View view) {
744 | if ("Download".equals(button_item.getText())) {
745 | download();
746 | } else {
747 | openWith();
748 | }
749 | }
750 | public void download() {
751 | if (!Util9.isPermissionGranted(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
752 | return ;
753 | }
754 | showProgressDialog("Downloading File.");
755 |
756 | final File localFile = new File(rootPath, filename);
757 |
758 | storageReference.child("files/"+realname).getFile(localFile).addOnSuccessListener(new OnSuccessListener() {
759 | @Override
760 | public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) {
761 | button_item.setText("Open File");
762 | hideProgressDialog();
763 | Log.e("DirectTalk9 ","local file created " +localFile.toString());
764 | }
765 | }).addOnFailureListener(new OnFailureListener() {
766 | @Override
767 | public void onFailure(@NonNull Exception exception) {
768 | Log.e("DirectTalk9 ","local file not created " +exception.toString());
769 | }
770 | });
771 | }
772 |
773 | public void openWith() {
774 | File newFile = new File(rootPath + filename);
775 | MimeTypeMap mime = MimeTypeMap.getSingleton();
776 | String ext = newFile.getName().substring(newFile.getName().lastIndexOf(".") + 1);
777 | String type = mime.getMimeTypeFromExtension(ext);
778 |
779 | Intent intent = new Intent(Intent.ACTION_VIEW);
780 | Uri uri;
781 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
782 | uri = FileProvider.getUriForFile(getContext(), getActivity().getPackageName() + ".provider", newFile);
783 |
784 | List resInfoList = getActivity().getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
785 | for (ResolveInfo resolveInfo : resInfoList) {
786 | String packageName = resolveInfo.activityInfo.packageName;
787 | getActivity().grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
788 | }
789 | }else {
790 | uri = Uri.fromFile(newFile);
791 | }
792 | intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
793 | intent.setDataAndType(uri, type);//"application/vnd.android.package-archive");
794 | startActivity(Intent.createChooser(intent, "Your title"));
795 | }
796 | };
797 | // photo view
798 | Button.OnClickListener imageClickListener = new View.OnClickListener() {
799 | public void onClick(View view) {
800 | Intent intent = new Intent(getContext(), ViewPagerActivity.class);
801 | intent.putExtra("roomID", roomID);
802 | intent.putExtra("realname", realname);
803 | startActivity(intent);
804 | }
805 | };
806 | }
807 |
808 | public void backPressed() {
809 | }
810 | }
811 |
--------------------------------------------------------------------------------