diff --git a/CHANGELOG.md b/CHANGELOG.md index ece7aa94..e3c7cb58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +### v3.5.0.beta.0 (Feb 6, 2023) with Chat SDK `v4.2.1` +* Support Notification Channel +A notification channel is a new group channel dedicated to receiving one way marketing and transactional messages.To allow users to view messages sent through Sendbird Message Builder with the correct rendering, you need to implement the notification channel view using `NotificationChannelActivity`, `NotificationChannelViewModel`, or `NotificationChannelFragment` + * Added `NotificationChannelActivity`, `NotificationChannelViewModel`, and `NotificationChannelFragment` + * Added `Action` class to pass data about events + * Added Notification module + * Added `NotificationChannelModule`, `NotificationMessageListComponent`, and `NotificationMessageListAdapter` + * Added `newNotificationChannelFragment(String, Bundle)` in `UIKitFragmentFactory` + * Added new style sets + * `Widget.Sendbird.Message.NotificationChannel` and `Widget.Sendbird.Dark.Message.NotificationChannel` + * `Widget.Sendbird.Message.MessageTemplateView` and `Widget.Sendbird.Dark.Message.MessageTemplateView` + * `Module.NotificationChannel` and `Module.Dark.NotificationChannel` + * `Component.Header.NotificationChannel` and `Component.Dark.Header.NotificationChannel` + * `Component.List.NotificationChannel` and `Component.Dark.List.NotificationChannel` + * `Component.Status.NotificationChannel` and `Component.Dark.Status.NotificationChannel` + ### v3.3.3 (Jan 19, 2023) with Chat SDK `v4.2.1` * Improved stability diff --git a/build.gradle b/build.gradle index 33a4203a..7e6ac940 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,10 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { ext.kotlin_version = '1.7.10' + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } } plugins { id 'com.android.application' version '7.2.2' apply false diff --git a/gradle.properties b/gradle.properties index 9697c2a1..5a3c80ee 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,5 +16,5 @@ org.gradle.jvmargs=-Xmx1536m # https://developer.android.com/topic/libraries/support-library/androidx-rn android.useAndroidX=true -UIKIT_VERSION = 3.3.3 +UIKIT_VERSION = 3.5.0.beta.0 UIKIT_VERSION_CODE = 1 diff --git a/uikit-custom-sample/google-services.json b/uikit-custom-sample/google-services.json deleted file mode 100644 index e1093147..00000000 --- a/uikit-custom-sample/google-services.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "project_info": { - "project_number": "45318056021", - "firebase_url": "https://sample-android-uikit.firebaseio.com", - "project_id": "sample-android-uikit", - "storage_bucket": "sample-android-uikit.appspot.com" - }, - "client": [ - { - "client_info": { - "mobilesdk_app_id": "1:45318056021:android:54331c18dc2a59c97e8cdc", - "android_client_info": { - "package_name": "com.sendbird.orange_sample" - } - }, - "oauth_client": [ - { - "client_id": "45318056021-79pcjv9ifmjj53nh5aphgkrdm0ns31f2.apps.googleusercontent.com", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "AIzaSyC71JAO4Gu5nHXTengB7qf4fptqx9EmVvI" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "45318056021-79pcjv9ifmjj53nh5aphgkrdm0ns31f2.apps.googleusercontent.com", - "client_type": 3 - } - ] - } - } - }, - { - "client_info": { - "mobilesdk_app_id": "1:45318056021:android:5e77060a31abc51a7e8cdc", - "android_client_info": { - "package_name": "com.sendbird.uikit.customsample" - } - }, - "oauth_client": [ - { - "client_id": "45318056021-79pcjv9ifmjj53nh5aphgkrdm0ns31f2.apps.googleusercontent.com", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "AIzaSyC71JAO4Gu5nHXTengB7qf4fptqx9EmVvI" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "45318056021-79pcjv9ifmjj53nh5aphgkrdm0ns31f2.apps.googleusercontent.com", - "client_type": 3 - } - ] - } - } - }, - { - "client_info": { - "mobilesdk_app_id": "1:45318056021:android:9351cabdc8e2eb1b7e8cdc", - "android_client_info": { - "package_name": "com.sendbird.uikit.sample" - } - }, - "oauth_client": [ - { - "client_id": "45318056021-79pcjv9ifmjj53nh5aphgkrdm0ns31f2.apps.googleusercontent.com", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "AIzaSyC71JAO4Gu5nHXTengB7qf4fptqx9EmVvI" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "45318056021-79pcjv9ifmjj53nh5aphgkrdm0ns31f2.apps.googleusercontent.com", - "client_type": 3 - } - ] - } - } - } - ], - "configuration_version": "1" -} \ No newline at end of file diff --git a/uikit-custom-sample/proguard-rules.pro b/uikit-custom-sample/proguard-rules.pro deleted file mode 100644 index 481bb434..00000000 --- a/uikit-custom-sample/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/uikit-custom-sample/src/androidTest/java/com/sendbird/uikit/customsample/ExampleInstrumentedTest.java b/uikit-custom-sample/src/androidTest/java/com/sendbird/uikit/customsample/ExampleInstrumentedTest.java deleted file mode 100644 index 76c800e2..00000000 --- a/uikit-custom-sample/src/androidTest/java/com/sendbird/uikit/customsample/ExampleInstrumentedTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.sendbird.uikit.customsample; - -import static org.junit.Assert.*; - -import android.content.Context; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.platform.app.InstrumentationRegistry; - -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Instrumented test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { - @Test - public void useAppContext() { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); - assertEquals("com.sendbird.uikit.customsample", appContext.getPackageName()); - } -} \ No newline at end of file diff --git a/uikit-custom-sample/src/main/AndroidManifest.xml b/uikit-custom-sample/src/main/AndroidManifest.xml deleted file mode 100644 index 8a0ac42e..00000000 --- a/uikit-custom-sample/src/main/AndroidManifest.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/BaseApplication.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/BaseApplication.java deleted file mode 100644 index 7251dfa4..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/BaseApplication.java +++ /dev/null @@ -1,243 +0,0 @@ -package com.sendbird.uikit.customsample; - - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.lifecycle.LiveData; -import androidx.lifecycle.MutableLiveData; -import androidx.multidex.MultiDexApplication; - -import com.sendbird.android.SendbirdChat; -import com.sendbird.android.exception.SendbirdError; -import com.sendbird.android.exception.SendbirdException; -import com.sendbird.android.handler.InitResultHandler; -import com.sendbird.android.params.ApplicationUserListQueryParams; -import com.sendbird.android.params.FileMessageCreateParams; -import com.sendbird.android.params.GroupChannelCreateParams; -import com.sendbird.android.params.GroupChannelUpdateParams; -import com.sendbird.android.params.OpenChannelCreateParams; -import com.sendbird.android.params.OpenChannelUpdateParams; -import com.sendbird.android.params.UserMessageCreateParams; -import com.sendbird.android.params.UserMessageUpdateParams; -import com.sendbird.android.user.User; -import com.sendbird.android.user.query.ApplicationUserListQuery; -import com.sendbird.uikit.SendbirdUIKit; -import com.sendbird.uikit.adapter.SendbirdUIKitAdapter; -import com.sendbird.uikit.consts.ReplyType; -import com.sendbird.uikit.consts.ThreadReplySelectType; -import com.sendbird.uikit.customsample.consts.InitState; -import com.sendbird.uikit.customsample.consts.StringSet; -import com.sendbird.uikit.customsample.fcm.MyFirebaseMessagingService; -import com.sendbird.uikit.customsample.models.CustomUser; -import com.sendbird.uikit.customsample.utils.PreferenceUtils; -import com.sendbird.uikit.customsample.utils.PushUtils; -import com.sendbird.uikit.interfaces.CustomParamsHandler; -import com.sendbird.uikit.interfaces.CustomUserListQueryHandler; -import com.sendbird.uikit.interfaces.OnListResultHandler; -import com.sendbird.uikit.interfaces.UserInfo; -import com.sendbird.uikit.log.Logger; -import com.sendbird.uikit.model.UserMentionConfig; - -import java.util.ArrayList; -import java.util.List; - -/** - * Base application to initialize Sendbird UIKit. - */ -public class BaseApplication extends MultiDexApplication { - - private static final String APP_ID = "2D7B4CDB-932F-4082-9B09-A1153792DC8D"; - private static final MutableLiveData initState = new MutableLiveData<>(); - - /** - * Initializes Sendbird UIKit - */ - @Override - public void onCreate() { - super.onCreate(); - PreferenceUtils.init(getApplicationContext()); - - SendbirdUIKit.init(new SendbirdUIKitAdapter() { - @NonNull - @Override - public String getAppId() { - return APP_ID; - } - - @Override - public String getAccessToken() { - return ""; - } - - @NonNull - @Override - public UserInfo getUserInfo() { - return new UserInfo() { - @NonNull - @Override - public String getUserId() { - return PreferenceUtils.getUserId(); - } - - @Override - public String getNickname() { - return PreferenceUtils.getNickname(); - } - - @Override - public String getProfileUrl() { - return PreferenceUtils.getProfileUrl(); - } - }; - } - - @NonNull - @Override - public InitResultHandler getInitResultHandler() { - return new InitResultHandler() { - @Override - public void onMigrationStarted() { - initState.setValue(InitState.MIGRATING); - } - - @Override - public void onInitFailed(@NonNull SendbirdException e) { - initState.setValue(InitState.FAILED); - } - - @Override - public void onInitSucceed() { - // register push notification - PushUtils.registerPushHandler(new MyFirebaseMessagingService()); - SendbirdUIKit.setDefaultThemeMode(SendbirdUIKit.ThemeMode.Light); - // set logger - SendbirdUIKit.setLogLevel(SendbirdUIKit.LogLevel.ALL); - // set whether to use user profile - SendbirdUIKit.setUseDefaultUserProfile(false); - // set reply type - SendbirdUIKit.setReplyType(ReplyType.THREAD); - SendbirdUIKit.setThreadReplySelectType(ThreadReplySelectType.THREAD); - // set custom user list query - SendbirdUIKit.setCustomUserListQueryHandler(getCustomUserListQuery()); - initState.setValue(InitState.SUCCEED); - } - }; - } - }, this); - - // set custom params - SendbirdUIKit.setCustomParamsHandler(new CustomParamsHandler() { - @Override - public void onBeforeCreateGroupChannel(@NonNull GroupChannelCreateParams groupChannelParams) { - // You can set GroupChannelParams globally before creating a channel. - } - - @Override - public void onBeforeUpdateGroupChannel(@NonNull GroupChannelUpdateParams groupChannelParams) { - // You can set GroupChannelParams globally before updating a channel. - } - - @Override - public void onBeforeSendUserMessage(@NonNull UserMessageCreateParams userMessageParams) { - // You can set UserMessageParams globally before sending a text message. - } - - @Override - public void onBeforeSendFileMessage(@NonNull FileMessageCreateParams fileMessageParams) { - // You can set FileMessageParams globally before sending a binary file message. - } - - @Override - public void onBeforeUpdateUserMessage(@NonNull UserMessageUpdateParams userMessageParams) { - // You can set UserMessageParams globally before updating a text message. - } - - @Override - public void onBeforeUpdateOpenChannel(@NonNull OpenChannelUpdateParams openChannelParams) { - // You can set OpenChannelParams globally before updating a channel. - } - - @Override - public void onBeforeCreateOpenChannel(@NonNull OpenChannelCreateParams params) { - // You can set OpenChannelCreateParams globally before creating a open channel. - params.setCustomType(StringSet.SB_COMMUNITY_TYPE); - } - }); - - // set custom UIKit fragment factory - SendbirdUIKit.setUIKitFragmentFactory(new CustomFragmentFactory()); - // set whether to use user mention - SendbirdUIKit.setUseUserMention(true); - // set the mention configuration - SendbirdUIKit.setMentionConfig(new UserMentionConfig.Builder() - .setMaxMentionCount(5) - .setMaxSuggestionCount(10) - .build()); - } - - /** - * Returns the state of the result from initialization of Sendbird UIKit. - * - * @return the {@link InitState} instance - */ - @NonNull - public static LiveData initStateChanges() { - return initState; - } - - /** - * Returns the user list query to be used to retrieve user list. - * - * @return the {@link CustomUserListQueryHandler} instance - */ - @NonNull - public static CustomUserListQueryHandler getCustomUserListQuery() { - return new CustomUserListQueryHandler() { - @Nullable - ApplicationUserListQuery userListQuery = null; - @Override - public void loadInitial(@NonNull OnListResultHandler handler) { - final ApplicationUserListQueryParams params = new ApplicationUserListQueryParams(); - params.setLimit(3); - userListQuery = SendbirdChat.createApplicationUserListQuery(params); - userListQuery.next((list, e) -> { - if (e != null || list == null) { - if (e != null && e.getCode() == SendbirdError.ERR_NON_AUTHORIZED) { - Logger.w("An error occurred because you don't have access to the user list in your application. " + - "In order to gain access, you can turn on this attribute in the Access Control List settings on Sendbird Dashboard."); - } - return; - } - - final List customUserList = new ArrayList<>(); - for (User user : list) { - customUserList.add(new CustomUser(user)); - } - handler.onResult(customUserList, null); - }); - } - - @Override - public void loadMore(@NonNull OnListResultHandler handler) { - if (userListQuery == null) return; - userListQuery.next((list, e) -> { - if (e != null || list == null) { - return; - } - - List customUserList = new ArrayList<>(); - for (User user : list) { - customUserList.add(new CustomUser(user)); - } - handler.onResult(customUserList, null); - }); - } - - @Override - public boolean hasMore() { - if (userListQuery == null) return false; - return userListQuery.getHasNext(); - } - }; - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/CustomFragmentFactory.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/CustomFragmentFactory.java deleted file mode 100644 index a00a9501..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/CustomFragmentFactory.java +++ /dev/null @@ -1,151 +0,0 @@ -package com.sendbird.uikit.customsample; - -import android.os.Bundle; - -import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; - -import com.sendbird.uikit.consts.CreatableChannelType; -import com.sendbird.uikit.customsample.groupchannel.fragments.CustomBannedUserListFragment; -import com.sendbird.uikit.customsample.groupchannel.fragments.CustomChannelFragment; -import com.sendbird.uikit.customsample.groupchannel.fragments.CustomChannelListFragment; -import com.sendbird.uikit.customsample.groupchannel.fragments.CustomChannelSettingsFragment; -import com.sendbird.uikit.customsample.groupchannel.fragments.CustomCreateChannelFragment; -import com.sendbird.uikit.customsample.groupchannel.fragments.CustomInviteUserFragment; -import com.sendbird.uikit.customsample.groupchannel.fragments.CustomMemberListFragment; -import com.sendbird.uikit.customsample.groupchannel.fragments.CustomMessageSearchFragment; -import com.sendbird.uikit.customsample.groupchannel.fragments.CustomModerationFragment; -import com.sendbird.uikit.customsample.groupchannel.fragments.CustomMutedMemberListFragment; -import com.sendbird.uikit.customsample.groupchannel.fragments.CustomOperatorListFragment; -import com.sendbird.uikit.customsample.groupchannel.fragments.CustomParticipantListFragment; -import com.sendbird.uikit.customsample.groupchannel.fragments.CustomRegisterOperatorFragment; -import com.sendbird.uikit.customsample.openchannel.CustomCreateOpenChannelFragment; -import com.sendbird.uikit.customsample.openchannel.CustomOpenChannelSettingsFragment; -import com.sendbird.uikit.fragments.ChannelFragment; -import com.sendbird.uikit.fragments.UIKitFragmentFactory; - -/** - * UIKit fragment factory implementation to provide customized fragments. - */ -public class CustomFragmentFactory extends UIKitFragmentFactory { - @NonNull - @Override - public Fragment newChannelListFragment(@NonNull Bundle args) { - final Fragment fragment = new CustomChannelListFragment(); - fragment.setArguments(args); - return fragment; - } - - @NonNull - @Override - public Fragment newChannelFragment(@NonNull String channelUrl, @NonNull Bundle args) { - return new ChannelFragment.Builder(channelUrl) - .setCustomFragment(new CustomChannelFragment()) - .withArguments(args) - .build(); - } - - @NonNull - @Override - public Fragment newMessageSearchFragment(@NonNull String channelUrl, @NonNull Bundle args) { - final Fragment fragment = new CustomMessageSearchFragment(); - fragment.setArguments(args); - return fragment; - } - - @NonNull - @Override - public Fragment newCreateChannelFragment(@NonNull CreatableChannelType channelType, @NonNull Bundle args) { - final Fragment fragment = new CustomCreateChannelFragment(); - fragment.setArguments(args); - return fragment; - } - - @NonNull - @Override - public Fragment newChannelSettingsFragment(@NonNull String channelUrl, @NonNull Bundle args) { - final Fragment fragment = new CustomChannelSettingsFragment(); - fragment.setArguments(args); - return fragment; - } - - @NonNull - @Override - public Fragment newInviteUserFragment(@NonNull String channelUrl, @NonNull Bundle args) { - final Fragment fragment = new CustomInviteUserFragment(); - fragment.setArguments(args); - return fragment; - } - - @NonNull - @Override - public Fragment newRegisterOperatorFragment(@NonNull String channelUrl, @NonNull Bundle args) { - final Fragment fragment = new CustomRegisterOperatorFragment(); - fragment.setArguments(args); - return fragment; - } - - @NonNull - @Override - public Fragment newModerationFragment(@NonNull String channelUrl, @NonNull Bundle args) { - final Fragment fragment = new CustomModerationFragment(); - fragment.setArguments(args); - return fragment; - } - - @NonNull - @Override - public Fragment newMemberListFragment(@NonNull String channelUrl, @NonNull Bundle args) { - final Fragment fragment = new CustomMemberListFragment(); - fragment.setArguments(args); - return fragment; - } - - @NonNull - @Override - public Fragment newBannedUserListFragment(@NonNull String channelUrl, @NonNull Bundle args) { - final Fragment fragment = new CustomBannedUserListFragment(); - fragment.setArguments(args); - return fragment; - } - - @NonNull - @Override - public Fragment newOperatorListFragment(@NonNull String channelUrl, @NonNull Bundle args) { - final Fragment fragment = new CustomOperatorListFragment(); - fragment.setArguments(args); - return fragment; - } - - @NonNull - @Override - public Fragment newMutedMemberListFragment(@NonNull String channelUrl, @NonNull Bundle args) { - final Fragment fragment = new CustomMutedMemberListFragment(); - fragment.setArguments(args); - return fragment; - } - - @NonNull - @Override - public Fragment newParticipantListFragment(@NonNull String channelUrl, @NonNull Bundle args) { - final Fragment fragment = new CustomParticipantListFragment(); - fragment.setArguments(args); - return fragment; - } - - @NonNull - @Override - public Fragment newOpenChannelSettingsFragment(@NonNull String channelUrl, @NonNull Bundle args) { - final CustomOpenChannelSettingsFragment fragment = new CustomOpenChannelSettingsFragment(); - fragment.setArguments(args); - return fragment; - } - - @NonNull - @Override - public Fragment newCreateOpenChannelFragment(@NonNull Bundle args) { - final CustomCreateOpenChannelFragment fragment = new CustomCreateOpenChannelFragment(); - fragment.setArguments(args); - return fragment; - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/HomeActivity.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/HomeActivity.java deleted file mode 100644 index 53ca3bd0..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/HomeActivity.java +++ /dev/null @@ -1,177 +0,0 @@ -package com.sendbird.uikit.customsample; - -import android.Manifest; -import android.app.NotificationManager; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.provider.Settings; -import android.view.View; - -import androidx.activity.result.ActivityResultLauncher; -import androidx.activity.result.contract.ActivityResultContracts; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; -import androidx.core.app.ActivityCompat; -import androidx.core.content.ContextCompat; -import androidx.core.content.PermissionChecker; - -import com.sendbird.android.SendbirdChat; -import com.sendbird.android.exception.SendbirdException; -import com.sendbird.android.handler.PushRequestCompleteHandler; -import com.sendbird.android.handler.UserEventHandler; -import com.sendbird.android.params.GroupChannelTotalUnreadMessageCountParams; -import com.sendbird.android.user.User; -import com.sendbird.uikit.BuildConfig; -import com.sendbird.uikit.SendbirdUIKit; -import com.sendbird.uikit.customsample.databinding.ActivityHomeBinding; -import com.sendbird.uikit.customsample.groupchannel.GroupChannelMainActivity; -import com.sendbird.uikit.customsample.openchannel.OpenChannelMainActivity; -import com.sendbird.uikit.customsample.utils.PreferenceUtils; -import com.sendbird.uikit.customsample.utils.PushUtils; -import com.sendbird.uikit.customsample.widgets.WaitingDialog; -import com.sendbird.uikit.utils.ContextUtils; - -import java.util.List; -import java.util.Locale; -import java.util.Map; - -/** - * Displays a channel select screen. - */ -public class HomeActivity extends AppCompatActivity { - private ActivityHomeBinding binding; - private static final String USER_EVENT_HANDLER_KEY = "USER_EVENT_HANDLER_KEY" + System.currentTimeMillis(); - - @NonNull - private final ActivityResultLauncher requestPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> {}); - @NonNull - private final ActivityResultLauncher appSettingLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), intent -> {}); - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - binding = ActivityHomeBinding.inflate(getLayoutInflater()); - View view = binding.getRoot(); - setContentView(view); - String sdkVersion = String.format(getResources().getString(R.string.text_version_info), BuildConfig.VERSION_NAME, SendbirdChat.getSdkVersion()); - binding.tvVersionInfo.setText(sdkVersion); - - binding.groupChannelButton.setOnClickListener(v -> clickGroupChannel()); - binding.openChannelButton.setOnClickListener(v -> clickOpenChannel()); - binding.btSignOut.setOnClickListener(v -> signOut()); - - binding.tvUnreadCount.setTextAppearance(this, R.style.SendbirdCaption3OnDark01); - binding.tvUnreadCount.setBackgroundResource(R.drawable.shape_badge_background); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - final String permission = Manifest.permission.POST_NOTIFICATIONS; - if (ContextCompat.checkSelfPermission(this, permission) == PermissionChecker.PERMISSION_GRANTED) { - return; - } - if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) { - showPermissionRationalePopup(permission); - return; - } - requestPermissionLauncher.launch(permission); - } - } - - @Override - protected void onResume() { - super.onResume(); - // initialize total unread count - SendbirdChat.getTotalUnreadMessageCount(new GroupChannelTotalUnreadMessageCountParams(), (totalCount, e) -> { - if (e != null) { - return; - } - - if (totalCount > 0) { - binding.tvUnreadCount.setVisibility(View.VISIBLE); - binding.tvUnreadCount.setText(totalCount > 99 ? - getString(R.string.text_tab_badge_max_count) : - String.valueOf(totalCount)); - } else { - binding.tvUnreadCount.setVisibility(View.GONE); - } - }); - - // register total unread count event - SendbirdChat.addUserEventHandler(USER_EVENT_HANDLER_KEY, new UserEventHandler() { - @Override - public void onFriendsDiscovered(@NonNull List list) {} - - @Override - public void onTotalUnreadMessageCountChanged(int totalCount, @NonNull Map totalCountByCustomType) { - if (totalCount > 0) { - binding.tvUnreadCount.setVisibility(View.VISIBLE); - binding.tvUnreadCount.setText(totalCount > 99 ? - getString(R.string.text_tab_badge_max_count) : - String.valueOf(totalCount)); - } else { - binding.tvUnreadCount.setVisibility(View.GONE); - } - } - }); - } - - @Override - protected void onPause() { - super.onPause(); - SendbirdChat.removeUserEventHandler(USER_EVENT_HANDLER_KEY); - } - - private void clickGroupChannel() { - Intent intent = new Intent(this, GroupChannelMainActivity.class); - startActivity(intent); - } - - private void clickOpenChannel() { - Intent intent = new Intent(this, OpenChannelMainActivity.class); - startActivity(intent); - } - - private void signOut() { - WaitingDialog.show(this); - PushUtils.unregisterPushHandler(new PushRequestCompleteHandler() { - @Override - public void onComplete(boolean isActive, String token) { - SendbirdUIKit.disconnect(() -> { - WaitingDialog.dismiss(); - PreferenceUtils.clearAll(); - NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - notificationManager.cancelAll(); - startActivity(new Intent(HomeActivity.this, LoginActivity.class)); - finish(); - }); - } - - @Override - public void onError(@NonNull SendbirdException e) { - WaitingDialog.dismiss(); - } - }); - } - - private void showPermissionRationalePopup(@NonNull String permission) { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(getString(com.sendbird.uikit.R.string.sb_text_dialog_permission_title)); - builder.setMessage(String.format(Locale.US, getString(R.string.sb_text_need_to_allow_permission_notification), ContextUtils.getApplicationName(this))); - builder.setPositiveButton(com.sendbird.uikit.R.string.sb_text_go_to_settings, (dialogInterface, i) -> { - Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); - intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); - intent.addCategory(Intent.CATEGORY_DEFAULT); - intent.setData(Uri.parse("package:" + getPackageName())); - intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); - intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); - appSettingLauncher.launch(intent); - }); - AlertDialog dialog = builder.create(); - dialog.show(); - dialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(ContextCompat.getColor(this, com.sendbird.uikit.R.color.secondary_300)); - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/LoginActivity.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/LoginActivity.java deleted file mode 100644 index f8daeb50..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/LoginActivity.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.sendbird.uikit.customsample; - -import android.content.Intent; -import android.os.Bundle; -import android.widget.EditText; -import android.widget.TextView; - -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; - -import com.sendbird.android.SendbirdChat; -import com.sendbird.uikit.BuildConfig; -import com.sendbird.uikit.SendbirdUIKit; -import com.sendbird.uikit.customsample.fcm.MyFirebaseMessagingService; -import com.sendbird.uikit.customsample.utils.PreferenceUtils; -import com.sendbird.uikit.customsample.utils.PushUtils; -import com.sendbird.uikit.customsample.widgets.WaitingDialog; -import com.sendbird.uikit.log.Logger; -import com.sendbird.uikit.utils.TextUtils; - -/** - * Displays a login screen. - */ -public class LoginActivity extends AppCompatActivity { - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.activity_login); - - EditText etUserId = findViewById(R.id.etUserId); - EditText etNickname = findViewById(R.id.etNickname); - TextView tvVersion = findViewById(R.id.tvVersionInfo); - - etUserId.setSelectAllOnFocus(true); - etNickname.setSelectAllOnFocus(true); - - String sdkVersion = String.format(getResources().getString(R.string.text_version_info), BuildConfig.VERSION_NAME, SendbirdChat.getSdkVersion()); - tvVersion.setText(sdkVersion); - - findViewById(R.id.btSignIn).setOnClickListener(v -> { - String userId = etUserId.getText().toString(); - // Remove all spaces from userID - String userIdString = userId.replaceAll("\\s", ""); - - String userNickname = etNickname.getText().toString(); - if (TextUtils.isEmpty(userId) || TextUtils.isEmpty(userNickname)) { - return; - } - - PreferenceUtils.setUserId(userIdString); - PreferenceUtils.setNickname(userNickname); - - WaitingDialog.show(this); - SendbirdUIKit.connect((user, e) -> { - if (e != null) { - Logger.e(e); - WaitingDialog.dismiss(); - PreferenceUtils.clearAll(); - return; - } - WaitingDialog.dismiss(); - PreferenceUtils.setUserId(userIdString); - PreferenceUtils.setNickname(userNickname); - PushUtils.registerPushHandler(new MyFirebaseMessagingService()); - Intent intent = new Intent(LoginActivity.this, HomeActivity.class); - startActivity(intent); - finish(); - }); - }); - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/SettingsFragment.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/SettingsFragment.java deleted file mode 100644 index 67713cc5..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/SettingsFragment.java +++ /dev/null @@ -1,366 +0,0 @@ -package com.sendbird.uikit.customsample; - -import static android.app.Activity.RESULT_OK; - -import android.Manifest; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.provider.Settings; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import androidx.activity.result.ActivityResultLauncher; -import androidx.activity.result.contract.ActivityResultContracts; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.widget.AppCompatImageView; -import androidx.appcompat.widget.SwitchCompat; -import androidx.appcompat.widget.Toolbar; -import androidx.core.content.ContextCompat; -import androidx.fragment.app.Fragment; - -import com.bumptech.glide.Glide; -import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.sendbird.android.SendbirdChat; -import com.sendbird.android.params.UserUpdateParams; -import com.sendbird.android.user.User; -import com.sendbird.uikit.SendbirdUIKit; -import com.sendbird.uikit.consts.DialogEditTextParams; -import com.sendbird.uikit.customsample.consts.StringSet; -import com.sendbird.uikit.customsample.utils.DrawableUtils; -import com.sendbird.uikit.customsample.utils.PreferenceUtils; -import com.sendbird.uikit.customsample.widgets.WaitingDialog; -import com.sendbird.uikit.interfaces.OnEditTextResultListener; -import com.sendbird.uikit.log.Logger; -import com.sendbird.uikit.model.DialogListItem; -import com.sendbird.uikit.utils.ContextUtils; -import com.sendbird.uikit.utils.DialogUtils; -import com.sendbird.uikit.utils.FileUtils; -import com.sendbird.uikit.utils.IntentUtils; -import com.sendbird.uikit.utils.PermissionUtils; -import com.sendbird.uikit.utils.TextUtils; - -import java.io.File; -import java.util.List; -import java.util.Locale; -import java.util.TimeZone; - -/** - * Displays a settings screen. - */ -public class SettingsFragment extends Fragment { - private final String[] REQUIRED_PERMISSIONS = PermissionUtils.CAMERA_PERMISSION; - - private AppCompatImageView profileImageView; - private TextView nicknameTextView; - private SwitchCompat disturbSwitch; - private Uri mediaUri; - - private final ActivityResultLauncher appSettingLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), intent -> { - if (getContext() == null) return; - final boolean hasPermission = PermissionUtils.hasPermissions(getContext(), REQUIRED_PERMISSIONS); - if (hasPermission) { - showMediaSelectDialog(); - } - }); - private final ActivityResultLauncher permissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), permissionResults -> { - if (PermissionUtils.getNotGrantedPermissions(permissionResults).isEmpty()) { - showMediaSelectDialog(); - } - }); - - private final ActivityResultLauncher takeCameraLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { - SendbirdChat.setAutoBackgroundDetection(true); - int resultCode = result.getResultCode(); - - if (resultCode != RESULT_OK || getContext() == null) return; - final Uri mediaUri = this.mediaUri; - if (mediaUri != null) { - final File file = FileUtils.uriToFile(getContext().getApplicationContext(), mediaUri); - updateUserProfileImage(file); - } - }); - private final ActivityResultLauncher getContentLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { - SendbirdChat.setAutoBackgroundDetection(true); - final Intent intent = result.getData(); - int resultCode = result.getResultCode(); - - if (resultCode != RESULT_OK || intent == null || getContext() == null) return; - final Uri mediaUri = intent.getData(); - if (mediaUri != null) { - final File file = FileUtils.uriToFile(getContext().getApplicationContext(), mediaUri); - updateUserProfileImage(file); - } - }); - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - SendbirdUIKit.connect(null); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - return inflater.inflate(R.layout.fragment_settings, container, false); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - initPage(view); - } - - @Override - public void onDestroy() { - super.onDestroy(); - SendbirdChat.setAutoBackgroundDetection(true); - } - - private void initPage(@NonNull View view) { - if (getContext() == null) { - return; - } - - Toolbar toolbar = view.findViewById(R.id.tbSettings); - toolbar.inflateMenu(R.menu.settings_menu); - toolbar.getMenu().findItem(R.id.action_edit_profile) - .getActionView().setOnClickListener(v -> { - Logger.d("++ edit button clicked"); - showEditProfileDialog(); - }); - - boolean useDoNotDisturb = true; - if (getArguments() != null) { - useDoNotDisturb = getArguments().getBoolean(StringSet.SETTINGS_USE_DO_NOT_DISTURB, true); - } - - profileImageView = view.findViewById(R.id.ivProfileView); - loadUserProfileUrl(PreferenceUtils.getProfileUrl()); - TextView userIdTextView = view.findViewById(R.id.tvUserId); - userIdTextView.setText(PreferenceUtils.getUserId()); - nicknameTextView = view.findViewById(R.id.tvNickname); - nicknameTextView.setText(PreferenceUtils.getNickname()); - - View disturbItem = view.findViewById(R.id.itemDisturb); - disturbItem.setVisibility(useDoNotDisturb ? View.VISIBLE : View.GONE); - if (useDoNotDisturb) { - disturbItem.setOnClickListener(v -> { - Logger.d("++ disturb clicked"); - updateDoNotDisturb(); - }); - - disturbSwitch = view.findViewById(R.id.scDisturbSwitch); - SendbirdChat.getDoNotDisturb((b, i, i1, i2, i3, s, e) -> { - PreferenceUtils.setDoNotDisturb(b); - disturbSwitch.setChecked(PreferenceUtils.getDoNotDisturb()); - }); - disturbSwitch.setOnClickListener(v -> { - Logger.d("++ disturb clicked"); - updateDoNotDisturb(); - }); - } - - View signOutItem = view.findViewById(R.id.itemHome); - signOutItem.setOnClickListener(v -> { - Logger.d("++ home clicked"); - if (getActivity() != null) { - getActivity().finish(); - } - }); - } - - private void showEditProfileDialog() { - if (getContext() == null || getFragmentManager() == null) return; - - DialogListItem[] items = { - new DialogListItem(R.string.text_settings_change_user_nickname), - new DialogListItem(R.string.text_settings_change_user_profile_image) - }; - - DialogUtils.showListBottomDialog(requireContext(), items, (v, p, item) -> { - final int key = item.getKey(); - if (key == R.string.text_settings_change_user_nickname) { - Logger.dev("change user nickname"); - OnEditTextResultListener listener = result -> { - if (TextUtils.isEmpty(result)) { - return; - } - updateUserNickname(result); - }; - - DialogEditTextParams params = new DialogEditTextParams(getString(com.sendbird.uikit.R.string.sb_text_channel_settings_change_channel_name_hint)); - params.setEnableSingleLine(true); - DialogUtils.showInputDialog( - requireContext(), - getString(R.string.text_settings_change_user_nickname), - params, listener, - getString(com.sendbird.uikit.R.string.sb_text_button_save), null, - getString(com.sendbird.uikit.R.string.sb_text_button_cancel), null); - } else if (key == R.string.text_settings_change_user_profile_image) { - Logger.dev("change user profile"); - - if (getContext() == null) return; - final boolean hasPermission = PermissionUtils.hasPermissions(getContext(), REQUIRED_PERMISSIONS); - if (hasPermission) { - showMediaSelectDialog(); - return; - } - - requestPermission(REQUIRED_PERMISSIONS); - } - }); - } - - private void requestPermission(@NonNull String[] permissions) { - if (getContext() == null || getActivity() == null) return; - // 1. check permission - final boolean hasPermission = PermissionUtils.hasPermissions(getContext(), permissions); - if (hasPermission) { - showMediaSelectDialog(); - return; - } - - // 2. determine whether rationale popup should show - final List deniedList = PermissionUtils.getExplicitDeniedPermissionList(getActivity(), permissions); - if (!deniedList.isEmpty()) { - showPermissionRationalePopup(deniedList.get(0)); - return; - } - // 3. request permission - this.permissionLauncher.launch(permissions); - } - - private void showPermissionRationalePopup(@NonNull String permission) { - AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); - builder.setTitle(getString(com.sendbird.uikit.R.string.sb_text_dialog_permission_title)); - builder.setMessage(getPermissionGuideMessage(requireContext(), permission)); - builder.setPositiveButton(com.sendbird.uikit.R.string.sb_text_go_to_settings, (dialogInterface, i) -> { - Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); - intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); - intent.addCategory(Intent.CATEGORY_DEFAULT); - intent.setData(Uri.parse("package:" + requireContext().getPackageName())); - intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); - intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); - appSettingLauncher.launch(intent); - }); - AlertDialog dialog = builder.create(); - dialog.show(); - dialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(ContextCompat.getColor(requireContext(), com.sendbird.uikit.R.color.secondary_300)); - } - - private void updateUserNickname(@NonNull String nickname) { - if (getContext() == null) return; - WaitingDialog.show(getContext()); - final UserUpdateParams params = new UserUpdateParams(); - params.setNickname(nickname); - SendbirdUIKit.updateUserInfo(params, e -> { - WaitingDialog.dismiss(); - if (e != null) { - Logger.e(e); - return; - } - - PreferenceUtils.setNickname(nickname); - nicknameTextView.setText(nickname); - }); - } - - private void updateUserProfileImage(@NonNull File profileImage) { - if (getContext() == null) return; - WaitingDialog.show(getContext()); - final UserUpdateParams params = new UserUpdateParams(); - params.setProfileImageFile(profileImage); - SendbirdUIKit.updateUserInfo(params, e -> { - WaitingDialog.dismiss(); - if (e != null) { - Logger.e(e); - return; - } - - final User currentUser = SendbirdChat.getCurrentUser(); - if (currentUser != null) { - final String profileUrl = currentUser.getProfileUrl(); - PreferenceUtils.setProfileUrl(profileUrl); - loadUserProfileUrl(profileUrl); - } - }); - } - - private void updateDoNotDisturb() { - disturbSwitch.setChecked(!PreferenceUtils.getDoNotDisturb()); - Logger.d("update do not disturb : " + !PreferenceUtils.getDoNotDisturb()); - SendbirdChat.setDoNotDisturb(!PreferenceUtils.getDoNotDisturb(), 0, 0, 23, 59, TimeZone.getDefault().getID(), e -> { - if (e != null) { - disturbSwitch.setChecked(PreferenceUtils.getDoNotDisturb()); - return; - } - Logger.d("update do not disturb on callback : " + !PreferenceUtils.getDoNotDisturb()); - PreferenceUtils.setDoNotDisturb(!PreferenceUtils.getDoNotDisturb()); - }); - } - - private void showMediaSelectDialog() { - if (getContext() == null) return; - DialogListItem[] items = { - new DialogListItem(com.sendbird.uikit.R.string.sb_text_channel_settings_change_channel_image_camera), - new DialogListItem(com.sendbird.uikit.R.string.sb_text_channel_settings_change_channel_image_gallery)}; - - DialogUtils.showListDialog(requireContext(), - getString(com.sendbird.uikit.R.string.sb_text_channel_settings_change_channel_image), - items, (v, p, item) -> { - try { - final int key = item.getKey(); - if (key == com.sendbird.uikit.R.string.sb_text_channel_settings_change_channel_image_camera) { - takeCamera(); - } else if (key == com.sendbird.uikit.R.string.sb_text_channel_settings_change_channel_image_gallery) { - takePhoto(); - } - } catch (Exception e) { - Logger.e(e); - } - }); - } - - private void takeCamera() { - SendbirdChat.setAutoBackgroundDetection(false); - this.mediaUri = FileUtils.createImageFileUri(requireContext()); - if (mediaUri == null) return; - Intent intent = IntentUtils.getCameraIntent(requireContext(), mediaUri); - if (IntentUtils.hasIntent(requireContext(), intent)) { - takeCameraLauncher.launch(intent); - } - } - - private void takePhoto() { - SendbirdChat.setAutoBackgroundDetection(false); - Intent intent = IntentUtils.getImageGalleryIntent(); - getContentLauncher.launch(intent); - } - - private static String getPermissionGuideMessage(@NonNull Context context, @NonNull String permission) { - int textResId; - if (Manifest.permission.CAMERA.equals(permission)) { - textResId = com.sendbird.uikit.R.string.sb_text_need_to_allow_permission_camera; - } else { - textResId = com.sendbird.uikit.R.string.sb_text_need_to_allow_permission_storage; - } - return String.format(Locale.US, context.getString(textResId), ContextUtils.getApplicationName(context)); - } - - private void loadUserProfileUrl(final String url) { - if (getContext() != null) { - Glide.with(requireContext()) - .load(url) - .circleCrop() - .diskCacheStrategy(DiskCacheStrategy.ALL) - .error(DrawableUtils.setTintList(requireContext(), R.drawable.icon_user, SendbirdUIKit.getDefaultThemeMode().getMonoTintResId())) - .into(profileImageView); - } - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/SplashActivity.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/SplashActivity.java deleted file mode 100644 index ae510e5f..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/SplashActivity.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.sendbird.uikit.customsample; - -import android.content.Intent; -import android.os.Bundle; -import android.text.TextUtils; - -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; - -import com.sendbird.uikit.SendbirdUIKit; -import com.sendbird.uikit.customsample.utils.PreferenceUtils; -import com.sendbird.uikit.customsample.widgets.WaitingDialog; -import com.sendbird.uikit.log.Logger; - -/** - * Displays a splash screen. - */ -public class SplashActivity extends AppCompatActivity { - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_splash); - - BaseApplication.initStateChanges().observe(this, initState -> { - Logger.i("++ init state : %s", initState); - WaitingDialog.dismiss(); - switch (initState) { - case MIGRATING: - WaitingDialog.show(SplashActivity.this); - break; - case FAILED: - case SUCCEED: - String userId = PreferenceUtils.getUserId(); - if (!TextUtils.isEmpty(userId)) { - SendbirdUIKit.connect((user, e) -> { - startActivity(getNextIntent()); - finish(); - }); - } else { - startActivity(getNextIntent()); - finish(); - } - break; - } - }); - } - - private Intent getNextIntent() { - String userId = PreferenceUtils.getUserId(); - if (!TextUtils.isEmpty(userId)) { - return new Intent(SplashActivity.this, HomeActivity.class); - } - - return new Intent(SplashActivity.this, LoginActivity.class); - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/consts/InitState.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/consts/InitState.java deleted file mode 100644 index 1ec94f92..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/consts/InitState.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.sendbird.uikit.customsample.consts; - -/** - * Used with Sendbird UIKit initialization. - */ -public enum InitState { - /** - * Indicates the migrating state. - */ - MIGRATING, - /** - * Indicates the failed state. - */ - FAILED, - /** - * Indicates the succeeded state. - */ - SUCCEED, - ; -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/consts/StringSet.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/consts/StringSet.java deleted file mode 100644 index be53cfc4..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/consts/StringSet.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.sendbird.uikit.customsample.consts; - -public class StringSet { - public static final String CHANNEL_NAME = "CHANNEL_NAME"; - public static final String CHANNEL_ID = "CHANNEL_ID"; - public static final String KEY_CHANNEL_URL = "KEY_CHANNEL_URL"; - public static final String PUSH_REDIRECT_CHANNEL = "PUSH_REDIRECT_CHANNEL"; - public static final String PUSH_REDIRECT_MESSAGE_ID = "PUSH_REDIRECT_MESSAGE_ID"; - public static final String SETTINGS_USE_DO_NOT_DISTURB = "SETTINGS_USE_DO_NOT_DISTURB"; - public static final String KEY_INPUT_TEXT = "KEY_INPUT_TEXT"; - - public static final String sendbird = "sendbird"; - public static final String channel = "channel"; - public static final String channel_url = "channel_url"; - public static final String message_id = "message_id"; - public static final String message = "message"; - public static final String sender = "sender"; - public static final String name = "name"; - public static final String highlight = "highlight"; - public static final String profile_url = "profile_url"; - - public static final String id = "id"; - public static final String tags = "tags"; - public static final String creator_info = "creator_info"; - public static final String thumbnail_url = "thumbnail_url"; - public static final String live_channel_url = "live_channel_url"; - public static final String emoji_type = "emoji_type"; - - public static final String SB_LIVE_TYPE = "SB_LIVE_TYPE"; - public static final String SB_COMMUNITY_TYPE = "SB_COMMUNITY_TYPE"; -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/fcm/MyFirebaseMessagingService.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/fcm/MyFirebaseMessagingService.java deleted file mode 100644 index 27ca31b1..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/fcm/MyFirebaseMessagingService.java +++ /dev/null @@ -1,131 +0,0 @@ -package com.sendbird.uikit.customsample.fcm; - -import android.annotation.SuppressLint; -import android.app.Notification; -import android.app.NotificationChannel; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.graphics.BitmapFactory; -import android.media.RingtoneManager; -import android.net.Uri; -import android.os.Build; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.app.NotificationCompat; -import androidx.core.content.ContextCompat; - -import com.google.firebase.messaging.RemoteMessage; -import com.sendbird.android.SendbirdChat; -import com.sendbird.android.push.SendbirdPushHandler; -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.customsample.consts.StringSet; -import com.sendbird.uikit.customsample.groupchannel.GroupChannelMainActivity; -import com.sendbird.uikit.log.Logger; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.concurrent.atomic.AtomicReference; - -/** - * Concrete implementation of a sendbird push handler. - */ -public class MyFirebaseMessagingService extends SendbirdPushHandler { - - private static final String TAG = "MyFirebaseMsgService"; - private static final AtomicReference pushToken = new AtomicReference<>(); - - @Override - public boolean isUniquePushToken() { - return false; - } - - @Override - public void onNewToken(@Nullable String token) { - Log.i(TAG, "onNewToken(" + token + ")"); - pushToken.set(token); - } - - /** - * Called when message is received. - * - * @param remoteMessage Object representing the message received from Firebase Cloud Messaging. - */ - @Override - public void onMessageReceived(@Nullable Context context, @Nullable RemoteMessage remoteMessage) { - if (context == null || remoteMessage == null) return; - Logger.d("From: " + remoteMessage.getFrom()); - if (remoteMessage.getData().size() > 0) { - Logger.d( "Message data payload: " + remoteMessage.getData()); - } - - // Check if message contains a notification payload. - if (remoteMessage.getNotification() != null) { - Logger.d( "Message Notification Body: " + remoteMessage.getNotification().getBody()); - } - - try { - if (remoteMessage.getData().containsKey(StringSet.sendbird)) { - String jsonStr = remoteMessage.getData().get(StringSet.sendbird); - SendbirdChat.markAsDelivered(remoteMessage.getData()); - if (jsonStr != null) { - sendNotification(context, new JSONObject(jsonStr)); - } - } - } catch (JSONException e) { - Logger.e(e); - } - } - - /** - * Create and show a simple notification containing the received FCM message. - * - * @param sendBird JSONObject payload from FCM - */ - public static void sendNotification(@NonNull Context context, @NonNull JSONObject sendBird) throws JSONException { - String message = sendBird.getString(StringSet.message); - JSONObject channel = sendBird.getJSONObject(StringSet.channel); - String channelUrl = channel.getString(StringSet.channel_url); - long messageId = sendBird.getLong(StringSet.message_id); - - String senderName = context.getString(R.string.app_name); - if (sendBird.has(StringSet.sender)) { - JSONObject sender = sendBird.getJSONObject(StringSet.sender); - senderName = sender.getString(StringSet.name); - } - - NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - - final String CHANNEL_ID = StringSet.CHANNEL_ID; - if (Build.VERSION.SDK_INT >= 26) { // Build.VERSION_CODES.O - NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, StringSet.CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH); - notificationManager.createNotificationChannel(mChannel); - } - - Intent intent = GroupChannelMainActivity.newRedirectToChannelIntent(context, channelUrl, messageId); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - @SuppressLint("UnspecifiedImmutableFlag") - PendingIntent pendingIntent = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M ? - PendingIntent.getActivity(context, (int) messageId /* Request code */, intent, PendingIntent.FLAG_IMMUTABLE) : - PendingIntent.getActivity(context, (int) messageId /* Request code */, intent, 0); - - Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); - NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, CHANNEL_ID) - .setSmallIcon(R.drawable.icon_push_lollipop) - .setColor(ContextCompat.getColor(context, R.color.primary_300)) // small icon background color - .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.icon_push_oreo)) - .setContentTitle(senderName) - .setAutoCancel(true) - .setSound(defaultSoundUri) - .setPriority(Notification.PRIORITY_MAX) - .setDefaults(Notification.DEFAULT_ALL) - .setContentIntent(pendingIntent); - notificationBuilder.setContentText(message); - - notificationManager.notify(String.valueOf(System.currentTimeMillis()), 0, notificationBuilder.build()); - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/GroupChannelMainActivity.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/GroupChannelMainActivity.java deleted file mode 100644 index 09c8f1dc..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/GroupChannelMainActivity.java +++ /dev/null @@ -1,179 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel; - -import static com.sendbird.uikit.customsample.consts.StringSet.PUSH_REDIRECT_CHANNEL; -import static com.sendbird.uikit.customsample.consts.StringSet.PUSH_REDIRECT_MESSAGE_ID; - -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentPagerAdapter; -import androidx.viewpager.widget.ViewPager; - -import com.google.android.material.tabs.TabLayout; -import com.sendbird.android.SendbirdChat; -import com.sendbird.android.handler.UserEventHandler; -import com.sendbird.android.params.GroupChannelTotalUnreadMessageCountParams; -import com.sendbird.android.user.User; -import com.sendbird.uikit.SendbirdUIKit; -import com.sendbird.uikit.activities.ChannelActivity; -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.customsample.SettingsFragment; -import com.sendbird.uikit.customsample.widgets.CustomTabView; - -import java.util.List; -import java.util.Map; -import java.util.Objects; - -/** - * Displays a group channel list screen. - */ -public class GroupChannelMainActivity extends AppCompatActivity { - private static final String USER_EVENT_HANDLER_KEY = "USER_EVENT_HANDLER_KEY"; - private CustomTabView unreadCountTab; - private ViewPager mainPage; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - initPage(); - } - - private void initPage() { - mainPage = findViewById(R.id.vpMain); - mainPage.setAdapter(new MainAdapter(getSupportFragmentManager(), FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)); - - TabLayout tabLayout = findViewById(R.id.tlMain); - tabLayout.setupWithViewPager(mainPage); - - unreadCountTab = new CustomTabView(this); - unreadCountTab.setBadgeVisibility(View.GONE); - unreadCountTab.setTitle(getString(R.string.text_tab_channels)); - unreadCountTab.setIcon(R.drawable.icon_chat_filled); - - CustomTabView settingsTab = new CustomTabView(this); - settingsTab.setBadgeVisibility(View.GONE); - settingsTab.setTitle(getString(R.string.text_tab_settings)); - settingsTab.setIcon(R.drawable.icon_settings_filled); - - Objects.requireNonNull(tabLayout.getTabAt(0)).setCustomView(unreadCountTab); - Objects.requireNonNull(tabLayout.getTabAt(1)).setCustomView(settingsTab); - - redirectChannelIfNeeded(getIntent()); - } - - @Override - protected void onResume() { - super.onResume(); - SendbirdChat.getTotalUnreadMessageCount(new GroupChannelTotalUnreadMessageCountParams(), (totalCount, e) -> { - if (e != null) { - return; - } - - if (totalCount > 0) { - unreadCountTab.setBadgeVisibility(View.VISIBLE); - unreadCountTab.setBadgeCount(totalCount > 99 ? - getString(R.string.text_tab_badge_max_count) : - String.valueOf(totalCount)); - } else { - unreadCountTab.setBadgeVisibility(View.GONE); - } - }); - - SendbirdChat.addUserEventHandler(USER_EVENT_HANDLER_KEY, new UserEventHandler() { - @Override - public void onFriendsDiscovered(@NonNull List list) {} - - @Override - public void onTotalUnreadMessageCountChanged(int totalCount, @NonNull Map totalCountByCustomType) { - if (totalCount > 0) { - unreadCountTab.setBadgeVisibility(View.VISIBLE); - unreadCountTab.setBadgeCount(totalCount > 99 ? - getString(R.string.text_tab_badge_max_count) : - String.valueOf(totalCount)); - } else { - unreadCountTab.setBadgeVisibility(View.GONE); - } - } - }); - } - - @Override - protected void onPause() { - super.onPause(); - SendbirdChat.removeUserEventHandler(USER_EVENT_HANDLER_KEY); - } - - @Override - protected void onNewIntent(@Nullable Intent intent) { - super.onNewIntent(intent); - redirectChannelIfNeeded(intent); - } - - @NonNull - public static Intent newRedirectToChannelIntent(@NonNull Context context, - @NonNull String channelUrl, - long messageId) { - Intent intent = new Intent(context, GroupChannelMainActivity.class); - intent.putExtra(PUSH_REDIRECT_CHANNEL, channelUrl); - intent.putExtra(PUSH_REDIRECT_MESSAGE_ID, messageId); - return intent; - } - - private void redirectChannelIfNeeded(Intent intent) { - if (intent == null) return; - - if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) { - intent.removeExtra(PUSH_REDIRECT_CHANNEL); - intent.removeExtra(PUSH_REDIRECT_MESSAGE_ID); - } - if (intent.hasExtra(PUSH_REDIRECT_CHANNEL)) { - String channelUrl = intent.getStringExtra(PUSH_REDIRECT_CHANNEL); - - if (intent.hasExtra(PUSH_REDIRECT_MESSAGE_ID)) { - long messageId = intent.getLongExtra(PUSH_REDIRECT_MESSAGE_ID, 0L); - if (messageId > 0L) { - startActivity(ChannelActivity.newRedirectToMessageThreadIntent(this, channelUrl, messageId)); - intent.removeExtra(PUSH_REDIRECT_MESSAGE_ID); - } - } else { - startActivity(ChannelActivity.newIntent(this, channelUrl)); - } - intent.removeExtra(PUSH_REDIRECT_CHANNEL); - } - } - - public void moveToSettings() { - mainPage.setCurrentItem(1); - } - - private static class MainAdapter extends FragmentPagerAdapter { - private static final int PAGE_SIZE = 2; - - public MainAdapter(@NonNull FragmentManager fm, int behavior) { - super(fm, behavior); - } - - @NonNull - @Override - public Fragment getItem(int position) { - if (position == 0) { - return SendbirdUIKit.getFragmentFactory().newChannelListFragment(new Bundle()); - } else { - return new SettingsFragment(); - } - } - - @Override - public int getCount() { - return PAGE_SIZE; - } - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomChannelHeaderComponent.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomChannelHeaderComponent.java deleted file mode 100644 index adadc4e9..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomChannelHeaderComponent.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.components; - -import android.content.Context; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.Toolbar; - -import com.sendbird.android.channel.GroupChannel; -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.log.Logger; -import com.sendbird.uikit.modules.components.ChannelHeaderComponent; - -/** - * Implements the customized ChannelHeaderComponent used in CustomChannelFragment. - */ -public class CustomChannelHeaderComponent extends ChannelHeaderComponent { - private Toolbar toolbar; - @Nullable - private View.OnClickListener searchButtonClickListener; - - public CustomChannelHeaderComponent() { - super(); - } - - @NonNull - @Override - public View onCreateView(@NonNull Context context, @NonNull LayoutInflater inflater, @NonNull ViewGroup parent, @Nullable Bundle args) { - toolbar = new Toolbar(context); - toolbar.setLayoutParams(new Toolbar.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) context.getResources().getDimension(R.dimen.sb_size_56))); - toolbar.setBackgroundColor(context.getResources().getColor(R.color.primary_300)); - toolbar.setTitleTextAppearance(context, R.style.SendbirdH1OnDark01); - toolbar.setSubtitleTextAppearance(context, R.style.SendbirdCaption1OnDark02); - toolbar.inflateMenu(R.menu.channel_menu); - toolbar.setNavigationIcon(R.drawable.icon_arrow_left); - toolbar.setNavigationOnClickListener(this::onLeftButtonClicked); - - toolbar.getMenu().findItem(R.id.action_settings) - .getActionView().setOnClickListener(v -> { - Logger.d("++ settings button clicked"); - onRightButtonClicked(v); - }); - - toolbar.getMenu().findItem(R.id.action_search) - .getActionView().setOnClickListener(v -> { - Logger.d("++ settings button clicked"); - if (searchButtonClickListener != null) searchButtonClickListener.onClick(v); - }); - return toolbar; - } - - @Override - public void notifyChannelChanged(@NonNull GroupChannel channel) { - toolbar.setTitle(channel.getName()); - } - - @Override - public void notifyHeaderDescriptionChanged(@Nullable String description) { - toolbar.setSubtitle(description); - } - - public void setSearchButtonClickListener(@Nullable View.OnClickListener searchButtonClickListener) { - this.searchButtonClickListener = searchButtonClickListener; - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomChannelListHeaderComponent.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomChannelListHeaderComponent.java deleted file mode 100644 index 87ea1354..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomChannelListHeaderComponent.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.components; - -import android.content.Context; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.Toolbar; - -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.log.Logger; -import com.sendbird.uikit.modules.components.HeaderComponent; - -/** - * Implements the customized HeaderComponent used in CustomChannelListFragment. - */ -public class CustomChannelListHeaderComponent extends HeaderComponent { - @Nullable - private View.OnClickListener settingsButtonClickListener; - - public CustomChannelListHeaderComponent() { - super(); - } - - @NonNull - @Override - public View onCreateView(@NonNull Context context, @NonNull LayoutInflater inflater, @NonNull ViewGroup parent, @Nullable Bundle savedInstanceState) { - Toolbar toolbar = new Toolbar(context); - toolbar.setLayoutParams(new Toolbar.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) context.getResources().getDimension(R.dimen.sb_size_56))); - toolbar.setBackgroundColor(context.getResources().getColor(R.color.primary_300)); - toolbar.setTitle(R.string.sb_text_header_channel_list); - toolbar.setTitleTextAppearance(context, R.style.SendbirdH1OnDark01); - toolbar.inflateMenu(R.menu.channels_menu); - - toolbar.getMenu().findItem(R.id.action_create_channel) - .getActionView().setOnClickListener(v -> { - Logger.d("++ create button clicked"); - onRightButtonClicked(v); - }); - toolbar.getMenu().findItem(R.id.action_settings) - .getActionView().setOnClickListener(v -> { - Logger.d("++ settings button clicked"); - if (settingsButtonClickListener != null) { - settingsButtonClickListener.onClick(v); - } - }); - return toolbar; - } - - public void setSettingsButtonClickListener(@Nullable View.OnClickListener settingsButtonClickListener) { - this.settingsButtonClickListener = settingsButtonClickListener; - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomChannelSettingsHeaderComponent.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomChannelSettingsHeaderComponent.java deleted file mode 100644 index 8bb903f6..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomChannelSettingsHeaderComponent.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.components; - -import android.content.Context; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.Toolbar; - -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.log.Logger; -import com.sendbird.uikit.modules.components.ChannelSettingsHeaderComponent; - -/** - * Implements the customized ChannelSettingsHeaderComponent. - */ -public class CustomChannelSettingsHeaderComponent extends ChannelSettingsHeaderComponent { - - public CustomChannelSettingsHeaderComponent() { - super(); - } - - @NonNull - @Override - public View onCreateView(@NonNull Context context, @NonNull LayoutInflater inflater, @NonNull ViewGroup parent, @Nullable Bundle savedInstanceState) { - Toolbar toolbar = new Toolbar(context); - toolbar.setLayoutParams(new Toolbar.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) context.getResources().getDimension(R.dimen.sb_size_56))); - toolbar.setBackgroundColor(context.getResources().getColor(R.color.primary_300)); - toolbar.setTitleTextAppearance(context, R.style.SendbirdH1OnDark01); - toolbar.setSubtitleTextAppearance(context, R.style.SendbirdCaption1OnDark02); - toolbar.setTitle(R.string.sb_text_header_channel_settings); - toolbar.inflateMenu(R.menu.channel_settings_menu); - toolbar.setNavigationIcon(R.drawable.icon_arrow_left); - toolbar.setNavigationOnClickListener(this::onLeftButtonClicked); - - toolbar.getMenu().findItem(R.id.action_edit) - .getActionView().setOnClickListener(v -> { - Logger.d("++ edit button clicked"); - onRightButtonClicked(v); - }); - return toolbar; - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomChannelSettingsMenuComponent.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomChannelSettingsMenuComponent.java deleted file mode 100644 index 1d8f0da4..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomChannelSettingsMenuComponent.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.components; - -import android.app.AlertDialog; -import android.content.Context; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.sendbird.android.channel.GroupChannel; -import com.sendbird.android.channel.Role; -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.modules.components.ChannelSettingsMenuComponent; - -/** - * Implements the customized ChannelSettingsMenuComponent. - */ -public class CustomChannelSettingsMenuComponent extends ChannelSettingsMenuComponent { - private View view; - - public CustomChannelSettingsMenuComponent() { - super(); - } - - @NonNull - @Override - public View onCreateView(@NonNull Context context, @NonNull LayoutInflater inflater, @NonNull ViewGroup parent, @Nullable Bundle savedInstanceState) { - view = inflater.inflate(R.layout.view_custom_channel_settings_menu, parent, false); - view.findViewById(R.id.tvModeration).setOnClickListener(v -> onMenuClicked(v, ChannelSettingsMenuComponent.Menu.MODERATIONS)); - view.findViewById(R.id.tvMemberList).setOnClickListener(v -> onMenuClicked(v, ChannelSettingsMenuComponent.Menu.MEMBERS)); - view.findViewById(R.id.tvLeaveChannel).setOnClickListener(this::showDeleteDialog); - return view; - } - - private void showDeleteDialog(@NonNull View v) { - new AlertDialog.Builder(v.getContext()) - .setTitle(R.string.text_dialog_leave_channel_title) - .setPositiveButton(R.string.sb_text_button_delete, (dialog, which) -> onMenuClicked(v, ChannelSettingsMenuComponent.Menu.LEAVE_CHANNEL)) - .setNegativeButton(R.string.sb_text_button_cancel, (dialog, which) -> dialog.dismiss()) - .show(); - } - - @Override - public void notifyChannelChanged(@NonNull GroupChannel channel) { - super.notifyChannelChanged(channel); - if (channel.getMyRole() == Role.OPERATOR) { - view.findViewById(R.id.moderationPanel).setVisibility(View.VISIBLE); - } else { - view.findViewById(R.id.moderationPanel).setVisibility(View.GONE); - } - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomMessageInputComponent.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomMessageInputComponent.java deleted file mode 100644 index f900b26e..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomMessageInputComponent.java +++ /dev/null @@ -1,279 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.components; - -import android.content.Context; -import android.os.Bundle; -import android.text.Editable; -import android.text.TextUtils; -import android.text.TextWatcher; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.CompoundButton; -import android.widget.EditText; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.AppCompatImageView; -import androidx.recyclerview.widget.GridLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import com.bumptech.glide.Glide; -import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.sendbird.android.channel.GroupChannel; -import com.sendbird.android.message.BaseMessage; -import com.sendbird.android.message.Emoji; -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.customsample.consts.StringSet; -import com.sendbird.uikit.customsample.databinding.ViewCustomChannelInputBinding; -import com.sendbird.uikit.interfaces.OnItemClickListener; -import com.sendbird.uikit.model.EmojiManager; -import com.sendbird.uikit.modules.components.MessageInputComponent; -import com.sendbird.uikit.widgets.MessageInputView; - -import java.util.List; - -/** - * Implements the customized MessageInputComponent. - */ -public class CustomMessageInputComponent extends MessageInputComponent { - private final static String MODE_MENU = "MODE_MENU"; - private final static String MODE_EMOJI = "MODE_EMOJI"; - @Nullable - private GroupChannel channel; - - private ViewCustomChannelInputBinding binding; - @Nullable - private CompoundButton.OnCheckedChangeListener highlightCheckedListener; - @Nullable - private View.OnClickListener menuCameraClickListener; - @Nullable - private View.OnClickListener menuPhotoClickListener; - @Nullable - private View.OnClickListener menuFileClickListener; - @Nullable - private OnItemClickListener emojiClickListener; - @NonNull - private MessageInputView.Mode mode = MessageInputView.Mode.DEFAULT; - @NonNull - private final EmojiAdapter adapter = new EmojiAdapter(); - private boolean isLeftClosed = false; - - public CustomMessageInputComponent() { - super(); - } - - @Nullable - @Override - public View getRootView() { - return binding.getRoot(); - } - - - @Nullable - @Override - public EditText getEditTextView() { - return binding.input; - } - - @Override - public void notifyChannelChanged(@NonNull GroupChannel channel) { - super.notifyChannelChanged(channel); - this.channel = channel; - } - - @NonNull - @Override - public View onCreateView(@NonNull Context context, @NonNull LayoutInflater inflater, @NonNull ViewGroup parent, @Nullable Bundle savedInstanceState) { - binding = ViewCustomChannelInputBinding.inflate(inflater, null, false); - binding.emojiPanel.setLayoutManager(new GridLayoutManager(context, 6)); - binding.emojiPanel.setAdapter(adapter); - binding.sendButton.setOnClickListener(this::onInputRightButtonClicked); - binding.leftButton.setOnClickListener(v -> { - if (isLeftClosed) { - requestInputMode(MessageInputView.Mode.DEFAULT); - if (channel != null) { - notifyDataChanged(null, channel); - } - } else { - requestInputMode(MODE_MENU); - } - }); - binding.emojiButton.setOnClickListener(v -> { - if (isLeftClosed) { - requestInputMode(MessageInputView.Mode.DEFAULT); - if (channel != null) { - notifyDataChanged(null, channel); - } - } else { - requestInputMode(MODE_EMOJI); - } - }); - binding.highlightSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { - if (highlightCheckedListener != null) highlightCheckedListener.onCheckedChanged(buttonView, isChecked); - }); - binding.camera.setOnClickListener(v -> { - if (menuCameraClickListener != null) menuCameraClickListener.onClick(v); - }); - binding.photo.setOnClickListener(v -> { - if (menuPhotoClickListener != null) menuPhotoClickListener.onClick(v); - }); - binding.file.setOnClickListener(v -> { - if (menuFileClickListener != null) menuFileClickListener.onClick(v); - }); - binding.input.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) {} - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - onInputTextChanged(s, start, before, count); - } - - @Override - public void afterTextChanged(Editable s) {} - }); - - adapter.emojiClickListener = emojiClickListener; - return binding.getRoot(); - } - - @Override - public void notifyDataChanged(@Nullable BaseMessage message, @NonNull GroupChannel channel, @NonNull String defaultValue) { - super.notifyDataChanged(message, channel, defaultValue); - - if (mode == MessageInputView.Mode.QUOTE_REPLY) { - if (message != null) { - binding.replyPanel.setText(message.getMessage()); - binding.replyPanel.setVisibility(View.VISIBLE); - } - } else if (mode == MessageInputView.Mode.EDIT) { - if (message != null) { - String customType = message.getCustomType(); - if (!TextUtils.isEmpty(customType) && - customType.equals(StringSet.highlight)) { - binding.highlightSwitch.setChecked(true); - } - binding.input.setText(message.getMessage()); - } - } else { - binding.input.setText(defaultValue); - } - } - - @Override - public void requestInputMode(@NonNull MessageInputView.Mode mode) { - final MessageInputView.Mode before = this.mode; - this.mode = mode; - - if (mode == MessageInputView.Mode.QUOTE_REPLY) { - binding.sendButton.setOnClickListener(this::onInputRightButtonClicked); - binding.emojiPanel.setVisibility(View.GONE); - binding.highlightSwitch.setChecked(false); - binding.menuPanel.setVisibility(View.GONE); - setLeftButton(true); - } else if (mode == MessageInputView.Mode.EDIT) { - binding.sendButton.setOnClickListener(this::onEditModeSaveButtonClicked); - binding.replyPanel.setVisibility(View.GONE); - binding.emojiPanel.setVisibility(View.GONE); - binding.menuPanel.setVisibility(View.GONE); - setLeftButton(false); - } else { - binding.sendButton.setOnClickListener(this::onInputRightButtonClicked); - binding.replyPanel.setVisibility(View.GONE); - binding.emojiPanel.setVisibility(View.GONE); - binding.menuPanel.setVisibility(View.GONE); - binding.highlightSwitch.setChecked(false); - setLeftButton(false); - } - - onInputModeChanged(before, mode); - } - - public void requestInputMode(@NonNull String mode) { - if (mode.equals(MODE_EMOJI)) { - binding.replyPanel.setVisibility(View.GONE); - binding.menuPanel.setVisibility(View.GONE); - binding.highlightSwitch.setChecked(false); - binding.emojiPanel.setVisibility(View.VISIBLE); - } else if (mode.equals(MODE_MENU)) { - binding.replyPanel.setVisibility(View.GONE); - binding.emojiPanel.setVisibility(View.GONE); - binding.highlightSwitch.setChecked(false); - binding.menuPanel.setVisibility(View.VISIBLE); - } - setLeftButton(true); - } - - public void setHighlightCheckedListener(@Nullable CompoundButton.OnCheckedChangeListener highlightCheckedListener) { - this.highlightCheckedListener = highlightCheckedListener; - } - - public void setMenuCameraClickListener(@Nullable View.OnClickListener menuCameraClickListener) { - this.menuCameraClickListener = menuCameraClickListener; - } - - public void setMenuPhotoClickListener(@Nullable View.OnClickListener menuPhotoClickListener) { - this.menuPhotoClickListener = menuPhotoClickListener; - } - - public void setMenuFileClickListener(@Nullable View.OnClickListener menuFileClickListener) { - this.menuFileClickListener = menuFileClickListener; - } - - public void setEmojiClickListener(@Nullable OnItemClickListener emojiClickListener) { - this.emojiClickListener = emojiClickListener; - adapter.emojiClickListener = emojiClickListener; - } - - private void setLeftButton(final boolean isLeftClosed) { - this.isLeftClosed = isLeftClosed; - if (isLeftClosed) { - binding.leftButton.setImageResource(R.drawable.icon_close); - } else { - binding.leftButton.setImageResource(R.drawable.icon_add); - } - } - - private static class EmojiAdapter extends RecyclerView.Adapter { - @NonNull - private final List emojis = EmojiManager.getInstance().getAllEmojis(); - @Nullable - private OnItemClickListener emojiClickListener; - - @NonNull - @Override - public EmojiViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - return new EmojiViewHolder(new AppCompatImageView(parent.getContext())); - } - - @Override - public void onBindViewHolder(@NonNull EmojiViewHolder holder, int position) { - final String emojiUrl = emojis.get(position).getUrl(); - holder.bind(emojiUrl); - holder.itemView.setOnClickListener(v -> { - if (emojiClickListener != null) { - emojiClickListener.onItemClick(v, position, emojiUrl); - } - }); - } - - @Override - public int getItemCount() { - return emojis.size(); - } - - static class EmojiViewHolder extends RecyclerView.ViewHolder { - public EmojiViewHolder(@NonNull View itemView) { - super(itemView); - } - - public void bind(@NonNull String emojiUrl) { - Glide.with(itemView) - .load(emojiUrl) - .circleCrop() - .diskCacheStrategy(DiskCacheStrategy.ALL) - .into((AppCompatImageView) itemView); - } - } - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomMessageSearchHeaderComponent.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomMessageSearchHeaderComponent.java deleted file mode 100644 index 7b789920..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomMessageSearchHeaderComponent.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.components; - -import android.content.Context; -import android.os.Bundle; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.EditText; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.modules.components.MessageSearchHeaderComponent; -import com.sendbird.uikit.utils.TextUtils; - -/** - * Implements the customized MessageSearchHeaderComponent. - */ -public class CustomMessageSearchHeaderComponent extends MessageSearchHeaderComponent { - private EditText input; - @Nullable - private View.OnClickListener cancelButtonClickListener; - - public CustomMessageSearchHeaderComponent() { - super(); - } - - @NonNull - @Override - public View onCreateView(@NonNull Context context, @NonNull LayoutInflater inflater, @NonNull ViewGroup parent, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.view_custom_message_search_header, parent, false); - input = view.findViewById(R.id.etSearch); - TextView cancel = view.findViewById(R.id.cancel); - - input.setOnKeyListener((v, keyCode, event) -> { - if (event.getAction() == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER) { - final String text = input.getText().toString(); - if (!TextUtils.isEmpty(text)) { - onSearchRequested(input.getText().toString()); - } - return true; - } - return false; - }); - cancel.setOnClickListener(v -> { - if (cancelButtonClickListener != null) cancelButtonClickListener.onClick(v); - }); - return view; - } - - public void setCancelButtonClickListener(@Nullable View.OnClickListener cancelButtonClickListener) { - this.cancelButtonClickListener = cancelButtonClickListener; - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomModerationHeaderComponent.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomModerationHeaderComponent.java deleted file mode 100644 index b880842f..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomModerationHeaderComponent.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.components; - -import android.content.Context; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.Toolbar; - -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.modules.components.HeaderComponent; - -/** - * Implements the customized HeaderComponent used in CustomModerationFragment. - */ -public class CustomModerationHeaderComponent extends HeaderComponent { - public CustomModerationHeaderComponent() { - super(); - } - - @NonNull - @Override - public View onCreateView(@NonNull Context context, @NonNull LayoutInflater inflater, @NonNull ViewGroup parent, @Nullable Bundle savedInstanceState) { - Toolbar toolbar = new Toolbar(context); - toolbar.setLayoutParams(new Toolbar.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) context.getResources().getDimension(R.dimen.sb_size_56))); - toolbar.setBackgroundColor(context.getResources().getColor(R.color.primary_300)); - toolbar.setTitle(R.string.sb_text_channel_settings_moderations); - toolbar.setTitleTextAppearance(context, R.style.SendbirdH1OnDark01); - toolbar.setNavigationIcon(R.drawable.icon_arrow_left); - toolbar.setNavigationOnClickListener(this::onLeftButtonClicked); - return toolbar; - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomModerationListComponent.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomModerationListComponent.java deleted file mode 100644 index 13ac550b..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomModerationListComponent.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.components; - -import android.content.Context; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.SwitchCompat; -import androidx.core.widget.NestedScrollView; - -import com.sendbird.android.channel.GroupChannel; -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.modules.components.ModerationListComponent; - -/** - * Implements the customized ModerationListComponent. - */ -public class CustomModerationListComponent extends ModerationListComponent { - private View view; - - public CustomModerationListComponent() { - super(); - } - - @Nullable - @Override - protected NestedScrollView getNestedScrollView() { - return view.findViewById(R.id.scroll_layout); - } - - @NonNull - @Override - public View onCreateView(@NonNull Context context, @NonNull LayoutInflater inflater, @NonNull ViewGroup parent, @Nullable Bundle savedInstanceState) { - view = inflater.inflate(R.layout.view_custom_moderation_list, parent, false); - view.findViewById(R.id.operators).setOnClickListener(v -> onMenuItemClicked(v, ModerationMenu.OPERATORS)); - view.findViewById(R.id.banned).setOnClickListener(v -> onMenuItemClicked(v, ModerationMenu.BANNED_MEMBERS)); - view.findViewById(R.id.muted).setOnClickListener(v -> onMenuItemClicked(v, ModerationMenu.MUTED_MEMBERS)); - view.findViewById(R.id.freeze).setOnClickListener(v -> onMenuItemClicked(v, ModerationMenu.FREEZE_CHANNEL)); - return view; - } - - @Override - public void notifyChannelChanged(@NonNull GroupChannel channel) { - ((SwitchCompat) view.findViewById(R.id.freezeSwitch)).setChecked(channel.isFrozen()); - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomSelectUserHeaderComponent.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomSelectUserHeaderComponent.java deleted file mode 100644 index 0767c8d5..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomSelectUserHeaderComponent.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.components; - -import android.content.Context; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.Toolbar; - -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.log.Logger; -import com.sendbird.uikit.modules.components.SelectUserHeaderComponent; - -/** - * Implements the customized SelectUserHeaderComponent. - */ -public class CustomSelectUserHeaderComponent extends SelectUserHeaderComponent { - private Toolbar toolbar; - - public CustomSelectUserHeaderComponent() { - super(); - } - - @NonNull - @Override - public View onCreateView(@NonNull Context context, @NonNull LayoutInflater inflater, @NonNull ViewGroup parent, @Nullable Bundle savedInstanceState) { - toolbar = new Toolbar(context); - toolbar.setLayoutParams(new Toolbar.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) context.getResources().getDimension(R.dimen.sb_size_56))); - toolbar.setBackgroundColor(context.getResources().getColor(R.color.primary_300)); - toolbar.setTitleTextAppearance(context, R.style.SendbirdH1OnDark01); - toolbar.setSubtitleTextAppearance(context, R.style.SendbirdCaption1OnDark02); - toolbar.setTitle(getParams().getTitle()); - toolbar.inflateMenu(R.menu.select_user_menu); - toolbar.setNavigationIcon(R.drawable.icon_arrow_left); - toolbar.setNavigationOnClickListener(this::onLeftButtonClicked); - - toolbar.getMenu().findItem(R.id.action_select) - .getActionView().setOnClickListener(v -> { - Logger.d("++ select button clicked"); - onRightButtonClicked(v); - }); - return toolbar; - } - - @Override - public void notifySelectedUserChanged(int count) { - if (count > 0) { - ((TextView) toolbar.getMenu().findItem(R.id.action_select) - .getActionView().findViewById(R.id.tvSelect)) - .setText(String.format(toolbar.getContext().getString(R.string.text_select_count), count)); - } else { - ((TextView) toolbar.getMenu().findItem(R.id.action_select) - .getActionView().findViewById(R.id.tvSelect)) - .setText(R.string.sb_text_button_selected); - } - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomUserTypedHeaderComponent.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomUserTypedHeaderComponent.java deleted file mode 100644 index fa9b1b99..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/CustomUserTypedHeaderComponent.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.components; - -import android.content.Context; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.AppCompatImageView; -import androidx.appcompat.widget.Toolbar; - -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.log.Logger; -import com.sendbird.uikit.modules.components.HeaderComponent; - -/** - * Implements the customized HeaderComponent used in the customized user typed fragments. - */ -public class CustomUserTypedHeaderComponent extends HeaderComponent { - - public CustomUserTypedHeaderComponent() { - super(); - } - - @NonNull - @Override - public View onCreateView(@NonNull Context context, @NonNull LayoutInflater inflater, @NonNull ViewGroup parent, @Nullable Bundle savedInstanceState) { - Toolbar toolbar = new Toolbar(context); - toolbar.setLayoutParams(new Toolbar.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) context.getResources().getDimension(R.dimen.sb_size_56))); - toolbar.setBackgroundColor(context.getResources().getColor(R.color.primary_300)); - toolbar.setTitle(getParams().getTitle()); - toolbar.setTitleTextAppearance(context, R.style.SendbirdH1OnDark01); - toolbar.inflateMenu(R.menu.user_typed_menu); - toolbar.setNavigationIcon(getParams().getLeftButtonIcon()); - toolbar.setNavigationOnClickListener(this::onLeftButtonClicked); - ((AppCompatImageView) toolbar.getMenu().findItem(R.id.action_right) - .getActionView().findViewById(R.id.rightIcon)).setImageDrawable(getParams().getRightButtonIcon()); - toolbar.getMenu().findItem(R.id.action_right) - .getActionView().setOnClickListener(v -> { - Logger.d("++ right button clicked"); - onRightButtonClicked(v); - }); - return toolbar; - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/adapters/CustomChannelListAdapter.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/adapters/CustomChannelListAdapter.java deleted file mode 100644 index 22148f28..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/adapters/CustomChannelListAdapter.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.components.adapters; - -import android.view.LayoutInflater; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; - -import com.sendbird.android.channel.GroupChannel; -import com.sendbird.uikit.activities.adapter.ChannelListAdapter; -import com.sendbird.uikit.activities.viewholder.BaseViewHolder; -import com.sendbird.uikit.customsample.databinding.ViewCustomChannelHolderBinding; -import com.sendbird.uikit.customsample.groupchannel.components.viewholders.CustomChannelViewHolder; - -/** - * Implements the customized ChannelListAdapter to adapt the customized channel list items. - */ -public class CustomChannelListAdapter extends ChannelListAdapter { - @NonNull - @Override - public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - return new CustomChannelViewHolder(ViewCustomChannelHolderBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); - } - - @Override - public void onBindViewHolder(@NonNull BaseViewHolder holder, int position) { - super.onBindViewHolder(holder, position); - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/adapters/CustomCreateChannelUserListAdapter.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/adapters/CustomCreateChannelUserListAdapter.java deleted file mode 100644 index 3f3667a6..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/adapters/CustomCreateChannelUserListAdapter.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.components.adapters; - -import static androidx.recyclerview.widget.RecyclerView.NO_POSITION; - -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.CheckBox; -import android.widget.TextView; - -import androidx.annotation.NonNull; - -import com.sendbird.uikit.activities.adapter.CreateChannelUserListAdapter; -import com.sendbird.uikit.activities.viewholder.BaseViewHolder; -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.interfaces.UserInfo; - -/** - * Implements the customized CreateChannelUserListAdapter to adapt the customized user list items. - */ -public class CustomCreateChannelUserListAdapter extends CreateChannelUserListAdapter { - @NonNull - @Override - public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_custom_user_holder, parent, false); - return new UserInfoViewHolder(view); - } - - private class UserInfoViewHolder extends BaseViewHolder { - private final TextView nickname; - private final CheckBox checkBox; - - /** - * Constructor - * - * @param itemView View to be displayed. - */ - public UserInfoViewHolder(@NonNull View itemView) { - super(itemView); - nickname = itemView.findViewById(R.id.tvNickname); - checkBox = itemView.findViewById(R.id.cbUser); - - itemView.setOnClickListener(v -> { - int userPosition = getAdapterPosition(); - if (userPosition != NO_POSITION) { - final UserInfo item = getItem(userPosition); - boolean isSelected = isSelected(item); - checkBox.setChecked(!isSelected); - } - }); - - checkBox.setOnCheckedChangeListener((buttonView, isSelected) -> { - int userPosition = getAdapterPosition(); - if (userPosition != NO_POSITION) { - final UserInfo userInfo = getItem(userPosition); - - if (isSelected && !isDisabled(userInfo)) { - selectedUserIdList.add(userInfo.getUserId()); - } else { - selectedUserIdList.remove(userInfo.getUserId()); - } - - if (userSelectChangedListener != null) { - userSelectChangedListener.onUserSelectChanged(selectedUserIdList, !isSelected); - } - } - }); - } - - @Override - public void bind(@NonNull UserInfo userInfo) { - nickname.setText(userInfo.getNickname()); - itemView.setEnabled(!isDisabled(userInfo)); - checkBox.setEnabled(!isDisabled(userInfo)); - checkBox.setChecked(isSelected(userInfo) || isDisabled(userInfo)); - } - } -} - diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/adapters/CustomInviteUserListAdapter.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/adapters/CustomInviteUserListAdapter.java deleted file mode 100644 index f5641422..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/adapters/CustomInviteUserListAdapter.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.components.adapters; - -import static androidx.recyclerview.widget.RecyclerView.NO_POSITION; - -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.CheckBox; -import android.widget.TextView; - -import androidx.annotation.NonNull; - -import com.sendbird.uikit.activities.adapter.InviteUserListAdapter; -import com.sendbird.uikit.activities.viewholder.BaseViewHolder; -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.interfaces.UserInfo; - -/** - * Implements the customized InviteUserListAdapter to adapt the customized user list items. - */ -public class CustomInviteUserListAdapter extends InviteUserListAdapter { - @NonNull - @Override - public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_custom_user_holder, parent, false); - return new UserInfoViewHolder(view); - } - - private class UserInfoViewHolder extends BaseViewHolder { - private final TextView nickname; - private final CheckBox checkBox; - - /** - * Constructor - * - * @param itemView View to be displayed. - */ - public UserInfoViewHolder(@NonNull View itemView) { - super(itemView); - nickname = itemView.findViewById(R.id.tvNickname); - checkBox = itemView.findViewById(R.id.cbUser); - - itemView.setOnClickListener(v -> { - int userPosition = getAdapterPosition(); - if (userPosition != NO_POSITION) { - final UserInfo item = getItem(userPosition); - boolean isSelected = isSelected(item); - checkBox.setChecked(!isSelected); - } - }); - - checkBox.setOnCheckedChangeListener((buttonView, isSelected) -> { - int userPosition = getAdapterPosition(); - if (userPosition != NO_POSITION) { - final UserInfo userInfo = getItem(userPosition); - - if (isSelected && !isDisabled(userInfo)) { - selectedUserIdList.add(userInfo.getUserId()); - } else { - selectedUserIdList.remove(userInfo.getUserId()); - } - - if (userSelectChangedListener != null) { - userSelectChangedListener.onUserSelectChanged(selectedUserIdList, !isSelected); - } - } - }); - } - - @Override - public void bind(@NonNull UserInfo userInfo) { - nickname.setText(userInfo.getNickname()); - itemView.setEnabled(!isDisabled(userInfo)); - checkBox.setEnabled(!isDisabled(userInfo)); - checkBox.setChecked(isSelected(userInfo) || isDisabled(userInfo)); - } - } -} - diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/adapters/CustomMemberListAdapter.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/adapters/CustomMemberListAdapter.java deleted file mode 100644 index 81bd0e8c..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/adapters/CustomMemberListAdapter.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.components.adapters; - -import static androidx.recyclerview.widget.RecyclerView.NO_POSITION; - -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.sendbird.android.channel.Role; -import com.sendbird.android.user.Member; -import com.sendbird.uikit.activities.adapter.MemberListAdapter; -import com.sendbird.uikit.activities.viewholder.BaseViewHolder; -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.interfaces.OnItemClickListener; - -import java.util.List; - -/** - * Implements the customized MemberListAdapter to adapt the customized member list items. - */ -public class CustomMemberListAdapter extends MemberListAdapter { - private OnItemClickListener actionItemClickListener; - private OnItemClickListener profileClickListener; - @NonNull - private Role myRole = Role.NONE; - - @NonNull - @Override - public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_custom_user_typed_holder, parent, false); - return new MemberPreviewHolder(v); - } - - @Override - public void setOnActionItemClickListener(@Nullable OnItemClickListener listener) { - this.actionItemClickListener = listener; - } - - @Override - public void setOnProfileClickListener(@Nullable OnItemClickListener profileClickListener) { - this.profileClickListener = profileClickListener; - } - - @Override - public void setItems(@NonNull List userList, @NonNull Role myRole) { - super.setItems(userList, myRole); - this.myRole = myRole; - } - - private class MemberPreviewHolder extends BaseViewHolder { - public MemberPreviewHolder(@NonNull View itemView) { - super(itemView); - - itemView.findViewById(R.id.ivAction).setOnClickListener(v -> { - int userPosition = getBindingAdapterPosition(); - if (userPosition != NO_POSITION && actionItemClickListener != null) { - actionItemClickListener.onItemClick(v, userPosition, getItem(userPosition)); - } - }); - - itemView.findViewById(R.id.ivProfile).setOnClickListener(v -> { - int userPosition = getBindingAdapterPosition(); - if (userPosition != NO_POSITION && profileClickListener != null) { - profileClickListener.onItemClick(v, userPosition, getItem(userPosition)); - } - }); - } - - @Override - public void bind(@NonNull Member user) { - if (myRole == Role.OPERATOR && actionItemClickListener != null) { - itemView.findViewById(R.id.ivAction).setVisibility(View.VISIBLE); - } else { - itemView.findViewById(R.id.ivAction).setVisibility(View.GONE); - } - ((TextView) itemView.findViewById(R.id.tvNickname)).setText(user.getNickname()); - } - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/adapters/CustomMessageListAdapter.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/adapters/CustomMessageListAdapter.java deleted file mode 100644 index bd8decfe..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/adapters/CustomMessageListAdapter.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.components.adapters; - -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; - -import com.sendbird.android.channel.GroupChannel; -import com.sendbird.android.message.BaseMessage; -import com.sendbird.android.message.UserMessage; -import com.sendbird.uikit.activities.adapter.MessageListAdapter; -import com.sendbird.uikit.activities.viewholder.MessageViewHolder; -import com.sendbird.uikit.customsample.consts.StringSet; -import com.sendbird.uikit.customsample.databinding.ViewEmojiMessageMeHolderBinding; -import com.sendbird.uikit.customsample.databinding.ViewEmojiMessageOtherHolderBinding; -import com.sendbird.uikit.customsample.databinding.ViewHighlightMessageMeHolderBinding; -import com.sendbird.uikit.customsample.databinding.ViewHighlightMessageOtherHolderBinding; -import com.sendbird.uikit.customsample.groupchannel.components.viewholders.EmojiMessageMeViewHolder; -import com.sendbird.uikit.customsample.groupchannel.components.viewholders.EmojiMessageOtherViewHolder; -import com.sendbird.uikit.customsample.groupchannel.components.viewholders.HighlightMessageMeViewHolder; -import com.sendbird.uikit.customsample.groupchannel.components.viewholders.HighlightMessageOtherViewHolder; -import com.sendbird.uikit.utils.MessageUtils; - -/** - * Implements the customized MessageListAdapter to adapt the customized message items. - */ -public class CustomMessageListAdapter extends MessageListAdapter { - - public static final int VIEW_HIGHLIGHT_MESSAGE_ME_TYPE = 1001; - public static final int VIEW_HIGHLIGHT_MESSAGE_OTHER_TYPE = 1002; - public static final int VIEW_EMOJI_MESSAGE_ME_TYPE = 1003; - public static final int VIEW_EMOJI_MESSAGE_OTHER_TYPE = 1004; - - public CustomMessageListAdapter(@NonNull GroupChannel channel, boolean useMessageGroupUI) { - super(channel, useMessageGroupUI); - } - - @NonNull - @Override - public MessageViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - // TODO: Create the custom ViewHolder and return it. - // Create your custom ViewHolder or call super.onCreateViewHolder() if you want to use the default. - if (viewType == VIEW_HIGHLIGHT_MESSAGE_ME_TYPE) { - LayoutInflater inflater = LayoutInflater.from(parent.getContext()); - return new HighlightMessageMeViewHolder(ViewHighlightMessageMeHolderBinding.inflate(inflater, parent, false)); - } else if (viewType == VIEW_HIGHLIGHT_MESSAGE_OTHER_TYPE) { - LayoutInflater inflater = LayoutInflater.from(parent.getContext()); - return new HighlightMessageOtherViewHolder(ViewHighlightMessageOtherHolderBinding.inflate(inflater, parent, false)); - } else if (viewType == VIEW_EMOJI_MESSAGE_ME_TYPE) { - LayoutInflater inflater = LayoutInflater.from(parent.getContext()); - return new EmojiMessageMeViewHolder(ViewEmojiMessageMeHolderBinding.inflate(inflater, parent, false)); - } else if (viewType == VIEW_EMOJI_MESSAGE_OTHER_TYPE) { - LayoutInflater inflater = LayoutInflater.from(parent.getContext()); - return new EmojiMessageOtherViewHolder(ViewEmojiMessageOtherHolderBinding.inflate(inflater, parent, false)); - } else { - return super.onCreateViewHolder(parent, viewType); - } - } - - @Override - public void onBindViewHolder(@NonNull MessageViewHolder holder, int position) { - // You must call the super. You can use methods that MessageViewHolder provides - super.onBindViewHolder(holder, position); - // TODO: Bind the custom ViewHolder - } - - @Override - public int getItemViewType(int position) { - BaseMessage message = getItem(position); - - String customType = message.getCustomType(); - - if (!TextUtils.isEmpty(customType) && - customType.equals(StringSet.emoji_type) && - message instanceof UserMessage) { - if (MessageUtils.isMine(message)) { - return VIEW_EMOJI_MESSAGE_ME_TYPE; - } else { - return VIEW_EMOJI_MESSAGE_OTHER_TYPE; - } - } else if (!TextUtils.isEmpty(customType) && - customType.equals(StringSet.highlight) && - message instanceof UserMessage) { - if (MessageUtils.isMine(message)) { - return VIEW_HIGHLIGHT_MESSAGE_ME_TYPE; - } else { - return VIEW_HIGHLIGHT_MESSAGE_OTHER_TYPE; - } - } - - return super.getItemViewType(position); - } -} - diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/adapters/CustomMessageSearchAdapter.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/adapters/CustomMessageSearchAdapter.java deleted file mode 100644 index ac2454eb..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/adapters/CustomMessageSearchAdapter.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.components.adapters; - -import static androidx.recyclerview.widget.RecyclerView.NO_POSITION; - -import android.text.format.DateUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.sendbird.android.message.BaseMessage; -import com.sendbird.uikit.activities.adapter.MessageSearchAdapter; -import com.sendbird.uikit.activities.viewholder.BaseViewHolder; -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.interfaces.OnItemClickListener; - -/** - * Implements the customized MessageSearchAdapter to adapt the customized search list items. - */ -public class CustomMessageSearchAdapter extends MessageSearchAdapter { - @Nullable - private OnItemClickListener itemClickListener; - - @NonNull - @Override - public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - return new SearchedMessageViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.view_custom_searched_message_holder, parent, false)); - } - - @Override - public void setOnItemClickListener(@Nullable OnItemClickListener listener) { - itemClickListener = listener; - } - - private class SearchedMessageViewHolder extends BaseViewHolder { - @NonNull - private final TextView nickname; - @NonNull - private final TextView message; - @NonNull - private final TextView sentAt; - - /** - * Constructor - * - * @param itemView View to be displayed. - */ - public SearchedMessageViewHolder(@NonNull View itemView) { - super(itemView); - nickname = itemView.findViewById(R.id.tvSender); - message = itemView.findViewById(R.id.tvMessage); - sentAt = itemView.findViewById(R.id.tvSentAt); - itemView.setOnClickListener(v -> { - int position = getAdapterPosition(); - if (position != NO_POSITION && itemClickListener != null) { - BaseMessage message = getItem(position); - itemClickListener.onItemClick(v, position, message); - } - }); - } - - @Override - public void bind(@NonNull BaseMessage message) { - nickname.setText(message.getSender().getNickname()); - this.message.setText(message.getMessage()); - sentAt.setText(DateUtils.formatDateTime(itemView.getContext(), message.getCreatedAt(), DateUtils.FORMAT_SHOW_TIME)); - } - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/adapters/CustomRegisterOperatorListAdapter.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/adapters/CustomRegisterOperatorListAdapter.java deleted file mode 100644 index 492c42e3..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/adapters/CustomRegisterOperatorListAdapter.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.components.adapters; - -import static androidx.recyclerview.widget.RecyclerView.NO_POSITION; - -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.CheckBox; -import android.widget.TextView; - -import androidx.annotation.NonNull; - -import com.sendbird.android.user.Member; -import com.sendbird.uikit.activities.adapter.RegisterOperatorListAdapter; -import com.sendbird.uikit.activities.viewholder.BaseViewHolder; -import com.sendbird.uikit.customsample.R; - -/** - * Implements the customized RegisterOperatorListAdapter to adapt the register as operators list items. - */ -public class CustomRegisterOperatorListAdapter extends RegisterOperatorListAdapter { - @NonNull - @Override - public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_custom_user_holder, parent, false); - return new UserInfoViewHolder(view); - } - - private class UserInfoViewHolder extends BaseViewHolder { - private final TextView nickname; - private final CheckBox checkBox; - - /** - * Constructor - * - * @param itemView View to be displayed. - */ - public UserInfoViewHolder(@NonNull View itemView) { - super(itemView); - nickname = itemView.findViewById(R.id.tvNickname); - checkBox = itemView.findViewById(R.id.cbUser); - - itemView.setOnClickListener(v -> { - int userPosition = getAdapterPosition(); - if (userPosition != NO_POSITION) { - final Member item = getItem(userPosition); - boolean isSelected = isSelected(item); - checkBox.setChecked(!isSelected); - } - }); - - checkBox.setOnCheckedChangeListener((buttonView, isSelected) -> { - int userPosition = getAdapterPosition(); - if (userPosition != NO_POSITION) { - final Member member = getItem(userPosition); - - if (isSelected && !isDisabled(member)) { - selectedUserIdList.add(member.getUserId()); - } else { - selectedUserIdList.remove(member.getUserId()); - } - - if (userSelectChangedListener != null) { - userSelectChangedListener.onUserSelectChanged(selectedUserIdList, !isSelected); - } - } - }); - } - - @Override - public void bind(@NonNull Member member) { - nickname.setText(member.getNickname()); - itemView.setEnabled(!isDisabled(member)); - checkBox.setEnabled(!isDisabled(member)); - checkBox.setChecked(isSelected(member) || isDisabled(member)); - } - } -} - diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/adapters/CustomSuggestedMentionListAdapter.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/adapters/CustomSuggestedMentionListAdapter.java deleted file mode 100644 index 0c2f3556..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/adapters/CustomSuggestedMentionListAdapter.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.components.adapters; - -import static androidx.recyclerview.widget.RecyclerView.NO_POSITION; - -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.sendbird.android.user.User; -import com.sendbird.uikit.activities.adapter.SuggestedMentionListAdapter; -import com.sendbird.uikit.activities.viewholder.BaseViewHolder; -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.interfaces.OnItemClickListener; - -/** - * Implements the customized SuggestedMentionListAdapter to adapt the suggested mention user list items. - */ -public class CustomSuggestedMentionListAdapter extends SuggestedMentionListAdapter { - @Nullable - private OnItemClickListener itemClickListener; - - @NonNull - @Override - public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_custom_suggested_mention_holder, parent, false); - return new SuggestedMentionViewHolder(view); - } - - @Override - public void setOnItemClickListener(@Nullable OnItemClickListener listener) { - this.itemClickListener = listener; - } - - private class SuggestedMentionViewHolder extends BaseViewHolder { - private final TextView nickname; - - /** - * Constructor - * - * @param itemView View to be displayed. - */ - public SuggestedMentionViewHolder(@NonNull View itemView) { - super(itemView); - nickname = itemView.findViewById(R.id.tvNickname); - - itemView.setOnClickListener(v -> { - int userPosition = getAdapterPosition(); - - if (userPosition != NO_POSITION) { - final User user = getItem(userPosition); - if (itemClickListener != null) { - itemClickListener.onItemClick(v, userPosition, user); - } - } - }); - } - - @Override - public void bind(@NonNull User user) { - nickname.setText(user.getNickname()); - } - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/viewholders/CustomChannelViewHolder.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/viewholders/CustomChannelViewHolder.java deleted file mode 100644 index 7716e8a6..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/viewholders/CustomChannelViewHolder.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.components.viewholders; - -import android.view.View; - -import androidx.annotation.NonNull; - -import com.sendbird.android.channel.GroupChannel; -import com.sendbird.uikit.activities.viewholder.BaseViewHolder; -import com.sendbird.uikit.customsample.databinding.ViewCustomChannelHolderBinding; -import com.sendbird.uikit.utils.ChannelUtils; -import com.sendbird.uikit.utils.DateUtils; - -/** - * ViewHolder to draw the channel list item for the GroupChannel. - */ -public class CustomChannelViewHolder extends BaseViewHolder { - private final ViewCustomChannelHolderBinding binding; - - public CustomChannelViewHolder(@NonNull ViewCustomChannelHolderBinding binding) { - super(binding.getRoot()); - this.binding = binding; - } - - @Override - public void bind(@NonNull GroupChannel channel) { - binding.tvTitle.setText(ChannelUtils.makeTitleText(binding.getRoot().getContext(), channel)); - - if (channel.getLastMessage() != null) { - binding.tvLastMessage.setText(channel.getLastMessage().getMessage()); - binding.tvUpdatedAt.setText(DateUtils.formatDateTime(binding.getRoot().getContext(), channel.getLastMessage().getCreatedAt())); - } - - if (channel.getMemberCount() > 2) { - binding.tvMemberCount.setVisibility(View.VISIBLE); - binding.tvMemberCount.setText(String.valueOf(channel.getMemberCount())); - } else { - binding.tvMemberCount.setVisibility(View.GONE); - } - - if (channel.getUnreadMessageCount() > 0) { - binding.tvUnreadCount.setVisibility(View.VISIBLE); - binding.tvUnreadCount.setText(String.valueOf(channel.getUnreadMessageCount())); - } else { - binding.tvUnreadCount.setVisibility(View.GONE); - } - - if (channel.getMyPushTriggerOption() == GroupChannel.PushTriggerOption.OFF) { - binding.ivPushEnabledIcon.setVisibility(View.VISIBLE); - } else { - binding.ivPushEnabledIcon.setVisibility(View.GONE); - } - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/viewholders/EmojiMessageMeViewHolder.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/viewholders/EmojiMessageMeViewHolder.java deleted file mode 100644 index a7cc05af..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/viewholders/EmojiMessageMeViewHolder.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.components.viewholders; - -import android.content.Context; -import android.text.format.DateUtils; -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.bumptech.glide.Glide; -import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.sendbird.android.channel.BaseChannel; -import com.sendbird.android.message.BaseMessage; -import com.sendbird.android.message.Reaction; -import com.sendbird.android.message.SendingStatus; -import com.sendbird.uikit.activities.viewholder.GroupChannelMessageViewHolder; -import com.sendbird.uikit.consts.ClickableViewIdentifier; -import com.sendbird.uikit.customsample.databinding.ViewEmojiMessageMeHolderBinding; -import com.sendbird.uikit.customsample.utils.DrawableUtils; -import com.sendbird.uikit.interfaces.OnItemClickListener; -import com.sendbird.uikit.interfaces.OnItemLongClickListener; -import com.sendbird.uikit.model.MessageListUIParams; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * ViewHolder to draw the emoji message sent from current user in the GroupChannel. - */ -public class EmojiMessageMeViewHolder extends GroupChannelMessageViewHolder { - private final ViewEmojiMessageMeHolderBinding binding; - - public EmojiMessageMeViewHolder(@NonNull ViewEmojiMessageMeHolderBinding binding) { - super(binding.getRoot()); - this.binding = binding; - } - - @Override - public void bind(@NonNull BaseChannel channel, @NonNull BaseMessage message, @NonNull MessageListUIParams params) { - Context context = binding.getRoot().getContext(); - boolean sendingState = message.getSendingStatus() == SendingStatus.SUCCEEDED; - - binding.tvSentAt.setVisibility(sendingState ? View.VISIBLE : View.GONE); - String sentAt = DateUtils.formatDateTime(context, message.getCreatedAt(), DateUtils.FORMAT_SHOW_TIME); - binding.tvSentAt.setText(sentAt); - DrawableUtils.drawStatus(binding.ivStatus, message); - Glide.with(itemView) - .load(message.getMessage()) - .circleCrop() - .diskCacheStrategy(DiskCacheStrategy.ALL) - .into(binding.ivEmoji); - - int paddingTop = context.getResources().getDimensionPixelSize(com.sendbird.uikit.R.dimen.sb_size_8); - int paddingBottom = context.getResources().getDimensionPixelSize(com.sendbird.uikit.R.dimen.sb_size_8); - binding.root.setPadding(binding.root.getPaddingLeft(), paddingTop, binding.root.getPaddingRight(), paddingBottom); - } - - @Override - public void setEmojiReaction(@NonNull List reactionList, @Nullable OnItemClickListener emojiReactionClickListener, @Nullable OnItemLongClickListener emojiReactionLongClickListener, @Nullable View.OnClickListener moreButtonClickListener) { - } - - @NonNull - @Override - public Map getClickableViewMap() { - return new ConcurrentHashMap() {{ - put(ClickableViewIdentifier.Chat.name(), binding.ivEmoji); - }}; - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/viewholders/EmojiMessageOtherViewHolder.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/viewholders/EmojiMessageOtherViewHolder.java deleted file mode 100644 index c1d74e1f..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/viewholders/EmojiMessageOtherViewHolder.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.components.viewholders; - -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.text.TextUtils; -import android.text.format.DateUtils; -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.bumptech.glide.Glide; -import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.bumptech.glide.request.RequestOptions; -import com.sendbird.android.channel.BaseChannel; -import com.sendbird.android.message.BaseMessage; -import com.sendbird.android.message.Reaction; -import com.sendbird.android.message.SendingStatus; -import com.sendbird.android.user.Sender; -import com.sendbird.uikit.activities.viewholder.GroupChannelMessageViewHolder; -import com.sendbird.uikit.consts.ClickableViewIdentifier; -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.customsample.databinding.ViewEmojiMessageOtherHolderBinding; -import com.sendbird.uikit.customsample.utils.DrawableUtils; -import com.sendbird.uikit.interfaces.OnItemClickListener; -import com.sendbird.uikit.interfaces.OnItemLongClickListener; -import com.sendbird.uikit.model.MessageListUIParams; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * ViewHolder to draw the emoji message sent from other users in the GroupChannel. - */ -public class EmojiMessageOtherViewHolder extends GroupChannelMessageViewHolder { - final private ViewEmojiMessageOtherHolderBinding binding; - - public EmojiMessageOtherViewHolder(@NonNull ViewEmojiMessageOtherHolderBinding binding) { - super(binding.getRoot()); - this.binding = binding; - } - - @Override - public void bind(@NonNull BaseChannel channel, @NonNull BaseMessage message, @NonNull MessageListUIParams params) { - Context context = binding.getRoot().getContext(); - boolean sendingState = message.getSendingStatus() == SendingStatus.SUCCEEDED; - binding.tvSentAt.setVisibility(sendingState ? View.VISIBLE : View.GONE); - String sentAt = DateUtils.formatDateTime(context, message.getCreatedAt(), DateUtils.FORMAT_SHOW_TIME); - binding.tvSentAt.setText(sentAt); - - Sender sender = message.getSender(); - String nickname = sender == null || TextUtils.isEmpty(sender.getNickname()) ? - context.getString(R.string.sb_text_channel_list_title_unknown) : - sender.getNickname(); - binding.tvNickname.setText(nickname); - - String url = ""; - if (sender != null && !TextUtils.isEmpty(sender.getProfileUrl())) { - url = sender.getProfileUrl(); - } - - Drawable errorIcon = DrawableUtils.createOvalIcon(binding.getRoot().getContext(), - R.color.background_300, R.drawable.icon_user, R.color.ondark_01); - Glide.with(context) - .load(url) - .diskCacheStrategy(DiskCacheStrategy.ALL) - .error(errorIcon) - .apply(RequestOptions.circleCropTransform()) - .into(binding.ivProfileView); - - Glide.with(itemView) - .load(message.getMessage()) - .circleCrop() - .diskCacheStrategy(DiskCacheStrategy.ALL) - .into(binding.ivEmoji); - } - - @NonNull - @Override - public Map getClickableViewMap() { - return new ConcurrentHashMap() {{ - put(ClickableViewIdentifier.Chat.name(),binding.ivEmoji); - put(ClickableViewIdentifier.Profile.name(), binding.ivProfileView); - }}; - } - - @Override - public void setEmojiReaction(@NonNull List reactionList, @Nullable OnItemClickListener emojiReactionClickListener, @Nullable OnItemLongClickListener emojiReactionLongClickListener, @Nullable View.OnClickListener moreButtonClickListener) {} -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/viewholders/HighlightMessageMeViewHolder.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/viewholders/HighlightMessageMeViewHolder.java deleted file mode 100644 index a4ffe3fe..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/viewholders/HighlightMessageMeViewHolder.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.components.viewholders; - -import android.content.Context; -import android.text.format.DateUtils; -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.sendbird.android.channel.BaseChannel; -import com.sendbird.android.message.BaseMessage; -import com.sendbird.android.message.Reaction; -import com.sendbird.android.message.SendingStatus; -import com.sendbird.uikit.activities.viewholder.GroupChannelMessageViewHolder; -import com.sendbird.uikit.consts.ClickableViewIdentifier; -import com.sendbird.uikit.customsample.databinding.ViewHighlightMessageMeHolderBinding; -import com.sendbird.uikit.customsample.utils.DrawableUtils; -import com.sendbird.uikit.interfaces.OnItemClickListener; -import com.sendbird.uikit.interfaces.OnItemLongClickListener; -import com.sendbird.uikit.model.MessageListUIParams; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * ViewHolder to draw the highlight message sent from current user in the GroupChannel. - */ -public class HighlightMessageMeViewHolder extends GroupChannelMessageViewHolder { - private final ViewHighlightMessageMeHolderBinding binding; - - public HighlightMessageMeViewHolder(@NonNull ViewHighlightMessageMeHolderBinding binding) { - super(binding.getRoot()); - this.binding = binding; - } - - @Override - public void bind(@NonNull BaseChannel channel, @NonNull BaseMessage message, @NonNull MessageListUIParams params) { - Context context = binding.getRoot().getContext(); - boolean sendingState = message.getSendingStatus() == SendingStatus.SUCCEEDED; - - binding.tvSentAt.setVisibility(sendingState ? View.VISIBLE : View.GONE); - String sentAt = DateUtils.formatDateTime(context, message.getCreatedAt(), DateUtils.FORMAT_SHOW_TIME); - binding.tvSentAt.setText(sentAt); - DrawableUtils.drawStatus(binding.ivStatus, message); - binding.tvMessage.setText(message.getMessage()); - - int paddingTop = context.getResources().getDimensionPixelSize(com.sendbird.uikit.R.dimen.sb_size_8); - int paddingBottom = context.getResources().getDimensionPixelSize(com.sendbird.uikit.R.dimen.sb_size_8); - binding.root.setPadding(binding.root.getPaddingLeft(), paddingTop, binding.root.getPaddingRight(), paddingBottom); - } - - @NonNull - @Override - public Map getClickableViewMap() { - return new ConcurrentHashMap() {{ - put(ClickableViewIdentifier.Chat.name(), binding.tvMessage); - }}; - } - - @Override - public void setEmojiReaction(@NonNull List reactionList, @Nullable OnItemClickListener emojiReactionClickListener, @Nullable OnItemLongClickListener emojiReactionLongClickListener, @Nullable View.OnClickListener moreButtonClickListener) {} -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/viewholders/HighlightMessageOtherViewHolder.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/viewholders/HighlightMessageOtherViewHolder.java deleted file mode 100644 index db738399..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/components/viewholders/HighlightMessageOtherViewHolder.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.components.viewholders; - -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.text.TextUtils; -import android.text.format.DateUtils; -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.bumptech.glide.Glide; -import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.bumptech.glide.request.RequestOptions; -import com.sendbird.android.channel.BaseChannel; -import com.sendbird.android.message.BaseMessage; -import com.sendbird.android.message.Reaction; -import com.sendbird.android.message.SendingStatus; -import com.sendbird.android.user.Sender; -import com.sendbird.uikit.activities.viewholder.GroupChannelMessageViewHolder; -import com.sendbird.uikit.consts.ClickableViewIdentifier; -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.customsample.databinding.ViewHighlightMessageOtherHolderBinding; -import com.sendbird.uikit.customsample.utils.DrawableUtils; -import com.sendbird.uikit.interfaces.OnItemClickListener; -import com.sendbird.uikit.interfaces.OnItemLongClickListener; -import com.sendbird.uikit.model.MessageListUIParams; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * ViewHolder to draw the highlight message sent from other users in the GroupChannel. - */ -public class HighlightMessageOtherViewHolder extends GroupChannelMessageViewHolder { - final private ViewHighlightMessageOtherHolderBinding binding; - - public HighlightMessageOtherViewHolder(@NonNull ViewHighlightMessageOtherHolderBinding binding) { - super(binding.getRoot()); - this.binding = binding; - } - - @Override - public void bind(@NonNull BaseChannel channel, @NonNull BaseMessage message, @NonNull MessageListUIParams params) { - Context context = binding.getRoot().getContext(); - boolean sendingState = message.getSendingStatus() == SendingStatus.SUCCEEDED; - binding.tvSentAt.setVisibility(sendingState ? View.VISIBLE : View.GONE); - String sentAt = DateUtils.formatDateTime(context, message.getCreatedAt(), DateUtils.FORMAT_SHOW_TIME); - binding.tvSentAt.setText(sentAt); - - Sender sender = message.getSender(); - String nickname = sender == null || TextUtils.isEmpty(sender.getNickname()) ? - context.getString(R.string.sb_text_channel_list_title_unknown) : - sender.getNickname(); - binding.tvNickname.setText(nickname); - - String url = ""; - if (sender != null && !TextUtils.isEmpty(sender.getProfileUrl())) { - url = sender.getProfileUrl(); - } - - Drawable errorIcon = DrawableUtils.createOvalIcon(binding.getRoot().getContext(), - R.color.background_300, R.drawable.icon_user, R.color.ondark_01); - Glide.with(context) - .load(url) - .diskCacheStrategy(DiskCacheStrategy.ALL) - .error(errorIcon) - .apply(RequestOptions.circleCropTransform()) - .into(binding.ivProfileView); - - binding.tvMessage.setText(message.getMessage()); - } - - @NonNull - @Override - public Map getClickableViewMap() { - return new ConcurrentHashMap() {{ - put(ClickableViewIdentifier.Chat.name(),binding.tvMessage); - put(ClickableViewIdentifier.Profile.name(), binding.ivProfileView); - }}; - } - - @Override - public void setEmojiReaction(@NonNull List reactionList, @Nullable OnItemClickListener emojiReactionClickListener, @Nullable OnItemLongClickListener emojiReactionLongClickListener, @Nullable View.OnClickListener moreButtonClickListener) {} -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomBannedUserListFragment.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomBannedUserListFragment.java deleted file mode 100644 index e817179e..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomBannedUserListFragment.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.fragments; - -import android.os.Bundle; - -import androidx.annotation.NonNull; -import androidx.core.content.res.ResourcesCompat; - -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.customsample.groupchannel.components.CustomUserTypedHeaderComponent; -import com.sendbird.uikit.fragments.BannedUserListFragment; -import com.sendbird.uikit.modules.BannedUserListModule; -import com.sendbird.uikit.modules.components.HeaderComponent; - -/** - * Implements the customized BannedUserListFragment. - */ -public class CustomBannedUserListFragment extends BannedUserListFragment { - @NonNull - @Override - protected BannedUserListModule onCreateModule(@NonNull Bundle args) { - BannedUserListModule module = super.onCreateModule(args); - module.setHeaderComponent(new CustomUserTypedHeaderComponent()); - return module; - } - - @Override - protected void onConfigureParams(@NonNull BannedUserListModule module, @NonNull Bundle args) { - super.onConfigureParams(module, args); - - HeaderComponent.Params headerParams = module.getHeaderComponent().getParams(); - if (isFragmentAlive()) { - headerParams.setTitle(requireContext().getString(R.string.sb_text_menu_banned_users)); - headerParams.setLeftButtonIcon(ResourcesCompat.getDrawable(getResources(), R.drawable.icon_arrow_left, null)); - headerParams.setUseRightButton(false); - } - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomChannelFragment.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomChannelFragment.java deleted file mode 100644 index 88d86bcc..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomChannelFragment.java +++ /dev/null @@ -1,118 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.fragments; - -import android.os.Bundle; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.lifecycle.ViewModelProvider; - -import com.sendbird.android.channel.GroupChannel; -import com.sendbird.android.params.FileMessageCreateParams; -import com.sendbird.android.params.UserMessageCreateParams; -import com.sendbird.android.params.UserMessageUpdateParams; -import com.sendbird.uikit.activities.MessageSearchActivity; -import com.sendbird.uikit.consts.StringSet; -import com.sendbird.uikit.customsample.groupchannel.components.CustomChannelHeaderComponent; -import com.sendbird.uikit.customsample.groupchannel.components.CustomMessageInputComponent; -import com.sendbird.uikit.customsample.groupchannel.components.adapters.CustomMessageListAdapter; -import com.sendbird.uikit.customsample.groupchannel.components.adapters.CustomSuggestedMentionListAdapter; -import com.sendbird.uikit.customsample.groupchannel.viewmodels.CustomChannelViewModel; -import com.sendbird.uikit.customsample.groupchannel.viewmodels.ViewModelFactory; -import com.sendbird.uikit.customsample.models.CustomMessageType; -import com.sendbird.uikit.fragments.ChannelFragment; -import com.sendbird.uikit.model.ReadyStatus; -import com.sendbird.uikit.modules.ChannelModule; -import com.sendbird.uikit.modules.components.ChannelHeaderComponent; -import com.sendbird.uikit.modules.components.MessageInputComponent; -import com.sendbird.uikit.vm.ChannelViewModel; -import com.sendbird.uikit.widgets.MessageInputView; - -/** - * Implements the customized ChannelFragment. - */ -public class CustomChannelFragment extends ChannelFragment { - @NonNull - private CustomMessageType customMessageType = CustomMessageType.NONE; - - @NonNull - @Override - protected ChannelModule onCreateModule(@NonNull Bundle args) { - ChannelModule module = super.onCreateModule(args); - module.setHeaderComponent(new CustomChannelHeaderComponent()); - module.setInputComponent(new CustomMessageInputComponent()); - return module; - } - - @NonNull - @Override - protected ChannelViewModel onCreateViewModel() { - final Bundle args = getArguments() == null ? new Bundle() : getArguments(); - final String channelUrl = args.getString(StringSet.KEY_CHANNEL_URL, ""); - return new ViewModelProvider(this, new ViewModelFactory(channelUrl)).get(channelUrl, CustomChannelViewModel.class); - } - - @Override - protected void onBeforeSendUserMessage(@NonNull UserMessageCreateParams params) { - super.onBeforeSendUserMessage(params); - params.setCustomType(customMessageType.getValue()); - } - - @Override - protected void onBeforeSendFileMessage(@NonNull FileMessageCreateParams params) { - super.onBeforeSendFileMessage(params); - params.setCustomType(customMessageType.getValue()); - } - - @Override - protected void onBeforeUpdateUserMessage(@NonNull UserMessageUpdateParams params) { - super.onBeforeUpdateUserMessage(params); - params.setCustomType(customMessageType.getValue()); - } - - @Override - protected void onBeforeReady(@NonNull ReadyStatus status, @NonNull ChannelModule module, @NonNull ChannelViewModel viewModel) { - super.onBeforeReady(status, module, viewModel); - - final GroupChannel channel = viewModel.getChannel(); - if (channel == null) return; - module.getMessageListComponent().setAdapter(new CustomMessageListAdapter(channel, true)); - } - - @Override - protected void onBindMessageInputComponent(@NonNull MessageInputComponent inputComponent, @NonNull ChannelViewModel viewModel, @Nullable GroupChannel channel) { - super.onBindMessageInputComponent(inputComponent, viewModel, channel); - - if (inputComponent instanceof CustomMessageInputComponent) { - CustomMessageInputComponent customInput = (CustomMessageInputComponent) getModule().getMessageInputComponent(); - customInput.setMenuCameraClickListener(v -> takeCamera()); - customInput.setMenuPhotoClickListener(v -> takePhoto()); - customInput.setMenuFileClickListener(v -> takeFile()); - customInput.setHighlightCheckedListener((buttonView, isChecked) -> - customMessageType = isChecked ? CustomMessageType.HIGHLIGHT : CustomMessageType.NONE); - customInput.setEmojiClickListener((view, position, url) -> { - final UserMessageCreateParams params = new UserMessageCreateParams(); - params.setMessage(url); - customMessageType = CustomMessageType.EMOJI; - sendUserMessage(params); - customInput.requestInputMode(MessageInputView.Mode.DEFAULT); - customMessageType = CustomMessageType.NONE; - }); - customInput.setUseSuggestedMentionListDivider(false); - customInput.setSuggestedMentionListAdapter(new CustomSuggestedMentionListAdapter()); - } - } - - @Override - protected void onBindChannelHeaderComponent(@NonNull ChannelHeaderComponent headerComponent, @NonNull ChannelViewModel viewModel, @Nullable GroupChannel channel) { - super.onBindChannelHeaderComponent(headerComponent, viewModel, channel); - - if (headerComponent instanceof CustomChannelHeaderComponent) { - CustomChannelHeaderComponent customHeader = (CustomChannelHeaderComponent) getModule().getHeaderComponent(); - customHeader.setSearchButtonClickListener(v -> { - if (isFragmentAlive() && channel != null) { - startActivity(MessageSearchActivity.newIntent(requireContext(), channel.getUrl())); - } - }); - } - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomChannelListFragment.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomChannelListFragment.java deleted file mode 100644 index 5bdddff8..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomChannelListFragment.java +++ /dev/null @@ -1,113 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.fragments; - -import android.app.AlertDialog; -import android.os.Bundle; -import android.widget.EditText; -import android.widget.Toast; - -import androidx.annotation.NonNull; - -import com.sendbird.android.channel.GroupChannel; -import com.sendbird.android.params.GroupChannelUpdateParams; -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.customsample.groupchannel.GroupChannelMainActivity; -import com.sendbird.uikit.customsample.groupchannel.components.CustomChannelListHeaderComponent; -import com.sendbird.uikit.customsample.groupchannel.components.adapters.CustomChannelListAdapter; -import com.sendbird.uikit.fragments.ChannelListFragment; -import com.sendbird.uikit.model.ReadyStatus; -import com.sendbird.uikit.modules.ChannelListModule; -import com.sendbird.uikit.modules.components.ChannelListComponent; -import com.sendbird.uikit.modules.components.HeaderComponent; -import com.sendbird.uikit.vm.ChannelListViewModel; - -/** - * Implements the customized ChannelListFragment. - */ -public class CustomChannelListFragment extends ChannelListFragment { - - @NonNull - @Override - protected ChannelListModule onCreateModule(@NonNull Bundle args) { - ChannelListModule module = super.onCreateModule(args); - module.setHeaderComponent(new CustomChannelListHeaderComponent()); - return module; - } - - @Override - protected void onConfigureParams(@NonNull ChannelListModule module, @NonNull Bundle args) { - super.onConfigureParams(module, args); - module.getParams().setUseHeader(true); - } - - @Override - protected void onBeforeReady(@NonNull ReadyStatus status, @NonNull ChannelListModule module, @NonNull ChannelListViewModel viewModel) { - super.onBeforeReady(status, module, viewModel); - module.getChannelListComponent().setAdapter(new CustomChannelListAdapter()); - module.getChannelListComponent().setOnItemLongClickListener((view, position, channel) -> showListContextMenu(channel)); - } - - private void showListContextMenu(@NonNull GroupChannel channel) { - if (getContext() == null) return; - - AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); - CharSequence titleItem = getString(R.string.sb_text_channel_settings_change_channel_name); - CharSequence leaveItem = getString(R.string.sb_text_channel_list_leave); - final boolean isOff = channel.getMyPushTriggerOption() == GroupChannel.PushTriggerOption.OFF; - CharSequence notificationItem = isOff ? getString(R.string.sb_text_channel_list_push_on) : - getString(R.string.sb_text_channel_list_push_off); - CharSequence[] items = {titleItem, leaveItem, notificationItem}; - builder.setItems(items, (dialog, which) -> { - dialog.dismiss(); - if (which == 0) { - showTitleChangeDialog(channel); - } else if (which == 1) { - getViewModel().leaveChannel(channel, e -> { - if (e == null) return; - Toast.makeText(requireContext(), R.string.sb_text_error_leave_channel, Toast.LENGTH_SHORT).show(); - }); - leaveChannel(channel); - } else { - getViewModel().setPushNotification(channel, isOff, - e -> { - if (e == null) return; - int errorString = isOff ? R.string.sb_text_error_push_notification_on : - R.string.sb_text_error_push_notification_off; - Toast.makeText(requireContext(), errorString, Toast.LENGTH_SHORT).show(); - }); - } - }); - builder.show(); - } - - private void showTitleChangeDialog(final GroupChannel channel) { - final EditText input = new EditText(getContext()); - AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); - builder.setView(input) - .setTitle(R.string.sb_text_channel_settings_change_channel_name) - .setPositiveButton(R.string.text_confirm, - (dialog, which) -> { - final GroupChannelUpdateParams params = new GroupChannelUpdateParams(); - params.setName(input.getText().toString()); - channel.updateChannel(params, null); - } - ); - builder.show(); - } - - @Override - protected void onBindHeaderComponent(@NonNull HeaderComponent headerComponent, @NonNull ChannelListViewModel viewModel) { - super.onBindHeaderComponent(headerComponent, viewModel); - ((CustomChannelListHeaderComponent) headerComponent).setSettingsButtonClickListener(v -> { - if (getActivity() instanceof GroupChannelMainActivity) { - ((GroupChannelMainActivity) getActivity()).moveToSettings(); - } - }); - } - - @Override - protected void onBindChannelListComponent(@NonNull ChannelListComponent channelListComponent, @NonNull ChannelListViewModel viewModel) { - super.onBindChannelListComponent(channelListComponent, viewModel); - channelListComponent.setOnItemLongClickListener((view, position, channel) - -> showListContextMenu(channel)); - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomChannelSettingsFragment.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomChannelSettingsFragment.java deleted file mode 100644 index e49f06c9..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomChannelSettingsFragment.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.fragments; - -import android.os.Bundle; - -import androidx.annotation.NonNull; - -import com.sendbird.uikit.customsample.groupchannel.components.CustomChannelSettingsHeaderComponent; -import com.sendbird.uikit.customsample.groupchannel.components.CustomChannelSettingsMenuComponent; -import com.sendbird.uikit.fragments.ChannelSettingsFragment; -import com.sendbird.uikit.model.ReadyStatus; -import com.sendbird.uikit.modules.ChannelSettingsModule; -import com.sendbird.uikit.vm.ChannelSettingsViewModel; - -/** - * Implements the customized ChannelSettingsFragment. - */ -public class CustomChannelSettingsFragment extends ChannelSettingsFragment { - @NonNull - @Override - protected ChannelSettingsModule onCreateModule(@NonNull Bundle args) { - ChannelSettingsModule module = super.onCreateModule(args); - module.setHeaderComponent(new CustomChannelSettingsHeaderComponent()); - module.setChannelSettingsMenuComponent(new CustomChannelSettingsMenuComponent()); - return module; - } - - @Override - protected void onReady(@NonNull ReadyStatus status, @NonNull ChannelSettingsModule module, @NonNull ChannelSettingsViewModel viewModel) { - super.onReady(status, module, viewModel); - - if (viewModel.getChannel() != null) { - module.getChannelSettingsMenuComponent().notifyChannelChanged(viewModel.getChannel()); - } - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomCreateChannelFragment.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomCreateChannelFragment.java deleted file mode 100644 index ad53121c..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomCreateChannelFragment.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.fragments; - -import android.os.Bundle; - -import androidx.annotation.NonNull; - -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.customsample.groupchannel.components.CustomSelectUserHeaderComponent; -import com.sendbird.uikit.customsample.groupchannel.components.adapters.CustomCreateChannelUserListAdapter; -import com.sendbird.uikit.fragments.CreateChannelFragment; -import com.sendbird.uikit.model.ReadyStatus; -import com.sendbird.uikit.modules.CreateChannelModule; -import com.sendbird.uikit.modules.components.SelectUserHeaderComponent; -import com.sendbird.uikit.vm.CreateChannelViewModel; - -/** - * Implements the customized CreateChannelFragment. - */ -public class CustomCreateChannelFragment extends CreateChannelFragment { - @NonNull - @Override - protected CreateChannelModule onCreateModule(@NonNull Bundle args) { - CreateChannelModule module = super.onCreateModule(args); - module.setHeaderComponent(new CustomSelectUserHeaderComponent()); - return module; - } - - @Override - protected void onConfigureParams(@NonNull CreateChannelModule module, @NonNull Bundle args) { - super.onConfigureParams(module, args); - SelectUserHeaderComponent.Params headerParams = module.getHeaderComponent().getParams(); - if (isFragmentAlive()) { - headerParams.setTitle(requireContext().getString(R.string.sb_text_header_create_channel)); - } - } - - @Override - protected void onBeforeReady(@NonNull ReadyStatus status, @NonNull CreateChannelModule module, @NonNull CreateChannelViewModel viewModel) { - super.onBeforeReady(status, module, viewModel); - module.getUserListComponent().setAdapter(new CustomCreateChannelUserListAdapter()); - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomInviteUserFragment.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomInviteUserFragment.java deleted file mode 100644 index 5d0cbbbb..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomInviteUserFragment.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.fragments; - -import android.os.Bundle; - -import androidx.annotation.NonNull; - -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.customsample.groupchannel.components.CustomSelectUserHeaderComponent; -import com.sendbird.uikit.customsample.groupchannel.components.adapters.CustomInviteUserListAdapter; -import com.sendbird.uikit.fragments.InviteUserFragment; -import com.sendbird.uikit.model.ReadyStatus; -import com.sendbird.uikit.modules.InviteUserModule; -import com.sendbird.uikit.modules.components.SelectUserHeaderComponent; -import com.sendbird.uikit.vm.InviteUserViewModel; - -/** - * Implements the customized InviteUserFragment. - */ -public class CustomInviteUserFragment extends InviteUserFragment { - @NonNull - @Override - protected InviteUserModule onCreateModule(@NonNull Bundle args) { - InviteUserModule module = super.onCreateModule(args); - module.setHeaderComponent(new CustomSelectUserHeaderComponent()); - return module; - } - - @Override - protected void onConfigureParams(@NonNull InviteUserModule module, @NonNull Bundle args) { - super.onConfigureParams(module, args); - SelectUserHeaderComponent.Params headerParams = module.getHeaderComponent().getParams(); - if (isFragmentAlive()) { - headerParams.setTitle(requireContext().getString(R.string.sb_text_header_invite_member)); - } - } - - @Override - protected void onBeforeReady(@NonNull ReadyStatus status, @NonNull InviteUserModule module, @NonNull InviteUserViewModel viewModel) { - super.onBeforeReady(status, module, viewModel); - module.getInviteUserListComponent().setAdapter(new CustomInviteUserListAdapter()); - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomMemberListFragment.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomMemberListFragment.java deleted file mode 100644 index a55b160c..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomMemberListFragment.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.fragments; - -import android.os.Bundle; - -import androidx.annotation.NonNull; -import androidx.core.content.res.ResourcesCompat; - -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.customsample.groupchannel.components.CustomUserTypedHeaderComponent; -import com.sendbird.uikit.customsample.groupchannel.components.adapters.CustomMemberListAdapter; -import com.sendbird.uikit.fragments.MemberListFragment; -import com.sendbird.uikit.model.ReadyStatus; -import com.sendbird.uikit.modules.MemberListModule; -import com.sendbird.uikit.modules.components.HeaderComponent; -import com.sendbird.uikit.vm.MemberListViewModel; - -/** - * Implements the customized MemberListFragment. - */ -public class CustomMemberListFragment extends MemberListFragment { - @NonNull - @Override - protected MemberListModule onCreateModule(@NonNull Bundle args) { - MemberListModule module = super.onCreateModule(args); - module.setHeaderComponent(new CustomUserTypedHeaderComponent()); - return module; - } - - @Override - protected void onConfigureParams(@NonNull MemberListModule module, @NonNull Bundle args) { - super.onConfigureParams(module, args); - HeaderComponent.Params headerParams = module.getHeaderComponent().getParams(); - if (isFragmentAlive()) { - headerParams.setTitle(requireContext().getString(R.string.sb_text_header_member_list)); - headerParams.setLeftButtonIcon(ResourcesCompat.getDrawable(getResources(), R.drawable.icon_arrow_left, null)); - headerParams.setRightButtonIcon(ResourcesCompat.getDrawable(getResources(), R.drawable.icon_plus, null)); - } - } - - @Override - protected void onBeforeReady(@NonNull ReadyStatus status, @NonNull MemberListModule module, @NonNull MemberListViewModel viewModel) { - super.onBeforeReady(status, module, viewModel); - module.getMemberListComponent().setAdapter(new CustomMemberListAdapter()); - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomMessageSearchFragment.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomMessageSearchFragment.java deleted file mode 100644 index 143d041e..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomMessageSearchFragment.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.fragments; - -import android.os.Bundle; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.sendbird.android.channel.GroupChannel; -import com.sendbird.uikit.customsample.groupchannel.components.CustomMessageSearchHeaderComponent; -import com.sendbird.uikit.customsample.groupchannel.components.adapters.CustomMessageSearchAdapter; -import com.sendbird.uikit.fragments.MessageSearchFragment; -import com.sendbird.uikit.model.ReadyStatus; -import com.sendbird.uikit.modules.MessageSearchModule; -import com.sendbird.uikit.modules.components.MessageSearchHeaderComponent; -import com.sendbird.uikit.vm.MessageSearchViewModel; - -/** - * Implements the customized MessageSearchFragment. - */ -public class CustomMessageSearchFragment extends MessageSearchFragment { - @NonNull - @Override - protected MessageSearchModule onCreateModule(@NonNull Bundle args) { - MessageSearchModule module = super.onCreateModule(args); - module.setHeaderComponent(new CustomMessageSearchHeaderComponent()); - return module; - } - - @Override - protected void onConfigureParams(@NonNull MessageSearchModule module, @NonNull Bundle args) { - super.onConfigureParams(module, args); - } - - @Override - protected void onBindHeaderComponent(@NonNull MessageSearchHeaderComponent headerComponent, @NonNull MessageSearchViewModel viewModel, @Nullable GroupChannel channel) { - super.onBindHeaderComponent(headerComponent, viewModel, channel); - if (headerComponent instanceof CustomMessageSearchHeaderComponent) { - CustomMessageSearchHeaderComponent customHeader = (CustomMessageSearchHeaderComponent) getModule().getHeaderComponent(); - customHeader.setCancelButtonClickListener(v -> shouldActivityFinish()); - } - } - - @Override - protected void onBeforeReady(@NonNull ReadyStatus status, @NonNull MessageSearchModule module, @NonNull MessageSearchViewModel viewModel) { - super.onBeforeReady(status, module, viewModel); - module.getMessageListComponent().setAdapter(new CustomMessageSearchAdapter()); - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomModerationFragment.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomModerationFragment.java deleted file mode 100644 index 2e92e655..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomModerationFragment.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.fragments; - -import android.os.Bundle; - -import androidx.annotation.NonNull; - -import com.sendbird.uikit.customsample.groupchannel.components.CustomModerationHeaderComponent; -import com.sendbird.uikit.customsample.groupchannel.components.CustomModerationListComponent; -import com.sendbird.uikit.fragments.ModerationFragment; -import com.sendbird.uikit.modules.ModerationModule; - -/** - * Implements the customized ModerationFragment. - */ -public class CustomModerationFragment extends ModerationFragment { - @NonNull - @Override - protected ModerationModule onCreateModule(@NonNull Bundle args) { - ModerationModule module = super.onCreateModule(args); - module.setHeaderComponent(new CustomModerationHeaderComponent()); - module.setModerationListComponent(new CustomModerationListComponent()); - return module; - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomMutedMemberListFragment.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomMutedMemberListFragment.java deleted file mode 100644 index bc963034..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomMutedMemberListFragment.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.fragments; - -import android.os.Bundle; - -import androidx.annotation.NonNull; -import androidx.core.content.res.ResourcesCompat; - -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.customsample.groupchannel.components.CustomUserTypedHeaderComponent; -import com.sendbird.uikit.fragments.MutedMemberListFragment; -import com.sendbird.uikit.modules.MutedMemberListModule; -import com.sendbird.uikit.modules.components.HeaderComponent; - -/** - * Implements the customized MutedMemberListFragment. - */ -public class CustomMutedMemberListFragment extends MutedMemberListFragment { - @NonNull - @Override - protected MutedMemberListModule onCreateModule(@NonNull Bundle args) { - MutedMemberListModule module = super.onCreateModule(args); - module.setHeaderComponent(new CustomUserTypedHeaderComponent()); - return module; - } - - @Override - protected void onConfigureParams(@NonNull MutedMemberListModule module, @NonNull Bundle args) { - super.onConfigureParams(module, args); - HeaderComponent.Params headerParams = module.getHeaderComponent().getParams(); - if (isFragmentAlive()) { - headerParams.setTitle(requireContext().getString(R.string.sb_text_header_member_list)); - headerParams.setLeftButtonIcon(ResourcesCompat.getDrawable(getResources(), R.drawable.icon_arrow_left, null)); - headerParams.setUseRightButton(false); - } - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomOperatorListFragment.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomOperatorListFragment.java deleted file mode 100644 index 71c93f6e..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomOperatorListFragment.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.fragments; - -import android.os.Bundle; - -import androidx.annotation.NonNull; -import androidx.core.content.res.ResourcesCompat; - -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.customsample.groupchannel.components.CustomUserTypedHeaderComponent; -import com.sendbird.uikit.fragments.OperatorListFragment; -import com.sendbird.uikit.modules.OperatorListModule; -import com.sendbird.uikit.modules.components.HeaderComponent; - -/** - * Implements the customized OperatorListFragment. - */ -public class CustomOperatorListFragment extends OperatorListFragment { - @NonNull - @Override - protected OperatorListModule onCreateModule(@NonNull Bundle args) { - OperatorListModule module = super.onCreateModule(args); - module.setHeaderComponent(new CustomUserTypedHeaderComponent()); - return module; - } - - @Override - protected void onConfigureParams(@NonNull OperatorListModule module, @NonNull Bundle args) { - super.onConfigureParams(module, args); - HeaderComponent.Params headerParams = module.getHeaderComponent().getParams(); - if (isFragmentAlive()) { - headerParams.setTitle(requireContext().getString(R.string.sb_text_menu_operators)); - headerParams.setLeftButtonIcon(ResourcesCompat.getDrawable(getResources(), R.drawable.icon_arrow_left, null)); - headerParams.setRightButtonIcon(ResourcesCompat.getDrawable(getResources(), R.drawable.icon_plus, null)); - } - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomParticipantListFragment.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomParticipantListFragment.java deleted file mode 100644 index d64ffa88..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomParticipantListFragment.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.fragments; - -import android.os.Bundle; - -import androidx.annotation.NonNull; -import androidx.core.content.res.ResourcesCompat; - -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.customsample.groupchannel.components.CustomUserTypedHeaderComponent; -import com.sendbird.uikit.fragments.ParticipantListFragment; -import com.sendbird.uikit.modules.ParticipantListModule; -import com.sendbird.uikit.modules.components.HeaderComponent; - -/** - * Implements the customized ParticipantListFragment. - */ -public class CustomParticipantListFragment extends ParticipantListFragment { - @NonNull - @Override - protected ParticipantListModule onCreateModule(@NonNull Bundle args) { - ParticipantListModule module = super.onCreateModule(args); - module.setHeaderComponent(new CustomUserTypedHeaderComponent()); - return module; - } - - @Override - protected void onConfigureParams(@NonNull ParticipantListModule module, @NonNull Bundle args) { - super.onConfigureParams(module, args); - HeaderComponent.Params headerParams = module.getHeaderComponent().getParams(); - if (isFragmentAlive()) { - headerParams.setTitle(requireContext().getString(R.string.sb_text_header_participants)); - headerParams.setLeftButtonIcon(ResourcesCompat.getDrawable(getResources(), R.drawable.icon_arrow_left, null)); - headerParams.setUseRightButton(false); - } - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomRegisterOperatorFragment.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomRegisterOperatorFragment.java deleted file mode 100644 index f6f2a5cd..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/fragments/CustomRegisterOperatorFragment.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.fragments; - -import android.os.Bundle; - -import androidx.annotation.NonNull; - -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.customsample.groupchannel.components.CustomSelectUserHeaderComponent; -import com.sendbird.uikit.customsample.groupchannel.components.adapters.CustomRegisterOperatorListAdapter; -import com.sendbird.uikit.fragments.RegisterOperatorFragment; -import com.sendbird.uikit.model.ReadyStatus; -import com.sendbird.uikit.modules.RegisterOperatorModule; -import com.sendbird.uikit.modules.components.SelectUserHeaderComponent; -import com.sendbird.uikit.vm.RegisterOperatorViewModel; - -/** - * Implements the customized RegisterOperatorFragment. - */ -public class CustomRegisterOperatorFragment extends RegisterOperatorFragment { - @NonNull - @Override - protected RegisterOperatorModule onCreateModule(@NonNull Bundle args) { - RegisterOperatorModule module = super.onCreateModule(args); - module.setHeaderComponent(new CustomSelectUserHeaderComponent()); - return module; - } - - @Override - protected void onConfigureParams(@NonNull RegisterOperatorModule module, @NonNull Bundle args) { - super.onConfigureParams(module, args); - SelectUserHeaderComponent.Params headerParams = module.getHeaderComponent().getParams(); - if (isFragmentAlive()) { - headerParams.setTitle(requireContext().getString(R.string.sb_text_header_set_operators)); - } - } - - @Override - protected void onBeforeReady(@NonNull ReadyStatus status, @NonNull RegisterOperatorModule module, @NonNull RegisterOperatorViewModel viewModel) { - super.onBeforeReady(status, module, viewModel); - module.getRegisterOperatorListComponent().setAdapter(new CustomRegisterOperatorListAdapter()); - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/viewmodels/CustomChannelViewModel.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/viewmodels/CustomChannelViewModel.java deleted file mode 100644 index 40b24838..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/viewmodels/CustomChannelViewModel.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.viewmodels; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.sendbird.android.params.MessageListParams; -import com.sendbird.uikit.customsample.models.CustomMessageType; -import com.sendbird.uikit.vm.ChannelViewModel; - -import java.util.ArrayList; -import java.util.List; - -/** - * Implements the customized ChannelViewModel to manage data related to the GroupChannel. - */ -public class CustomChannelViewModel extends ChannelViewModel { - public CustomChannelViewModel(@NonNull String channelUrl, @Nullable MessageListParams messageListParams) { - super(channelUrl, messageListParams); - } - - @NonNull - @Override - public MessageListParams createMessageListParams() { - final MessageListParams params = super.createMessageListParams(); - final List customTypes = new ArrayList<>(); - customTypes.add(CustomMessageType.NONE.getValue()); - customTypes.add(CustomMessageType.HIGHLIGHT.getValue()); - customTypes.add(CustomMessageType.EMOJI.getValue()); - params.setCustomTypes(customTypes); - return params; - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/viewmodels/ViewModelFactory.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/viewmodels/ViewModelFactory.java deleted file mode 100644 index 07ab9242..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/groupchannel/viewmodels/ViewModelFactory.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.sendbird.uikit.customsample.groupchannel.viewmodels; - -import androidx.annotation.NonNull; -import androidx.lifecycle.ViewModel; -import androidx.lifecycle.ViewModelProvider; - -import com.sendbird.android.params.MessageListParams; - -import java.util.Objects; - -/** - * Provides to create the customized ViewModels. - */ -public class ViewModelFactory extends ViewModelProvider.NewInstanceFactory { - private final Object[] params; - - public ViewModelFactory(@NonNull Object... params) { - this.params = params; - } - - @SuppressWarnings("unchecked") - @NonNull - @Override - public T create(@NonNull Class modelClass) { - if (modelClass.isAssignableFrom(CustomChannelViewModel.class)) { - return (T) new CustomChannelViewModel((String) Objects.requireNonNull(params)[0], params.length > 1 ? (MessageListParams) params[1] : null); - } else { - return super.create(modelClass); - } - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/models/CustomMessageType.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/models/CustomMessageType.java deleted file mode 100644 index ec6dada6..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/models/CustomMessageType.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.sendbird.uikit.customsample.models; - -import androidx.annotation.NonNull; - -import com.sendbird.uikit.customsample.consts.StringSet; - -/** - * Used with drawing messages by this type. - */ -public enum CustomMessageType { - /** - * Draws the UIKit default message type. - */ - NONE(""), - /** - * Draws the highlighted message type. - */ - HIGHLIGHT(StringSet.highlight), - /** - * Draws the emoji message type. - */ - EMOJI(StringSet.emoji_type); - - private final String value; - CustomMessageType(String value) { this.value = value; } - - @NonNull - public String getValue() { - return value; - } - - @NonNull - public static CustomMessageType from(@NonNull String value) { - for (CustomMessageType type : values()) { - if (type.value.equals(value)) { - return type; - } - } - return NONE; - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/models/CustomUser.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/models/CustomUser.java deleted file mode 100644 index e991113a..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/models/CustomUser.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.sendbird.uikit.customsample.models; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.sendbird.android.user.User; -import com.sendbird.uikit.interfaces.UserInfo; - -/** - * Model class for a user data to adapt UIKit UserInfo interface. - */ -public class CustomUser implements UserInfo { - User user; - - public CustomUser(@NonNull User user) { - this.user = user; - } - - @NonNull - @Override - public String getUserId() { - return user.getUserId(); - } - - @NonNull - @Override - public String getNickname() { - return user.getNickname(); - } - - @Nullable - @Override - public String getProfileUrl() { - return user.getProfileUrl(); - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/models/LiveStreamingChannelData.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/models/LiveStreamingChannelData.java deleted file mode 100644 index 6317f107..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/models/LiveStreamingChannelData.java +++ /dev/null @@ -1,176 +0,0 @@ -package com.sendbird.uikit.customsample.models; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.sendbird.uikit.customsample.consts.StringSet; -import com.sendbird.uikit.interfaces.UserInfo; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.ArrayList; -import java.util.List; - -/** - * Model class for a live streaming channel data. - */ -public class LiveStreamingChannelData { - private final String name; - private final List tags = new ArrayList<>(); - private final Creator creator; - private final String thumbnailUrl; - private final String liveUrl; - - public LiveStreamingChannelData(@NonNull JSONObject jsonObject) throws JSONException { - this.name = jsonObject.optString(StringSet.name); - this.creator = jsonObject.has(StringSet.creator_info) ? new Creator(jsonObject.getJSONObject(StringSet.creator_info)) : null; - JSONArray tagsJsonArray = jsonObject.optJSONArray(StringSet.tags); - if (tagsJsonArray != null) { - for (int i = 0; i < tagsJsonArray.length(); i++) { - this.tags.add(tagsJsonArray.opt(i).toString()); - } - } - - this.thumbnailUrl = jsonObject.optString(StringSet.thumbnail_url); - this.liveUrl = jsonObject.optString(StringSet.live_channel_url); - } - - @Nullable - public String getName() { - return name; - } - - @Nullable - public List getTags() { - return tags; - } - - @Nullable - public UserInfo getCreator() { - return creator; - } - - @Nullable - public String getThumbnailUrl() { - return thumbnailUrl; - } - - @Nullable - public String getLiveUrl() { - return liveUrl; - } - - private static class Creator implements UserInfo { - final String userId; - final String nickname; - final String profileUrl; - - public Creator(JSONObject jsonObject) { - userId = jsonObject.optString(StringSet.id); - nickname = jsonObject.optString(StringSet.name); - profileUrl = jsonObject.optString(StringSet.profile_url); - } - - @NonNull - @Override - public String getUserId() { - return userId; - } - - @Override - public String getNickname() { - return nickname; - } - - @Override - public String getProfileUrl() { - return profileUrl; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof Creator)) return false; - Creator creator = (Creator) o; - - if (!userId.equals(creator.userId)) { - return false; - } - - if (!nickname.equals(creator.nickname)) { - return false; - } - - return profileUrl.equals(creator.profileUrl); - } - - @Override - public int hashCode() { - int result = 17; - result = result * 31 + (userId != null ? userId.hashCode() : 0); - result = result * 31 + (nickname != null ? nickname.hashCode() : 0); - result = result * 31 + (profileUrl != null ? profileUrl.hashCode() : 0); - return result; - } - - @NonNull - @Override - public String toString() { - return "Creator{" + - "userId='" + userId + '\'' + - ", nickname='" + nickname + '\'' + - ", profileUrl='" + profileUrl + '\'' + - '}'; - } - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof LiveStreamingChannelData)) return false; - LiveStreamingChannelData that = (LiveStreamingChannelData) o; - - if (!name.equals(that.name)) { - return false; - } - - if (!tags.equals(that.tags)) { - return false; - } - - if (!creator.equals(that.creator)) { - return false; - } - - if (!thumbnailUrl.equals(that.thumbnailUrl)) { - return false; - } - - return liveUrl.equals(that.liveUrl); - } - - @Override - public int hashCode() { - int result = 17; - result = result * 31 + (name != null ? name.hashCode() : 0); - result = result * 31 + tags.hashCode(); - result = result * 31 + (creator != null ? creator.hashCode() : 0); - result = result * 31 + (thumbnailUrl != null ? thumbnailUrl.hashCode() : 0); - result = result * 31 + (liveUrl != null ? liveUrl.hashCode() : 0); - return result; - } - - @NonNull - @Override - public String toString() { - return "LiveStreamingChannelData{" + - "name='" + name + '\'' + - ", tags=" + tags + - ", creator=" + creator + - ", thumbnailUrl='" + thumbnailUrl + '\'' + - ", liveUrl='" + liveUrl + '\'' + - '}'; - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/CustomCreateOpenChannelFragment.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/CustomCreateOpenChannelFragment.java deleted file mode 100644 index 8d904e54..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/CustomCreateOpenChannelFragment.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.sendbird.uikit.customsample.openchannel; - -import static android.app.Activity.RESULT_OK; - -import androidx.annotation.NonNull; - -import com.sendbird.android.channel.OpenChannel; -import com.sendbird.uikit.activities.OpenChannelActivity; -import com.sendbird.uikit.customsample.openchannel.community.CommunityActivity; -import com.sendbird.uikit.fragments.CreateOpenChannelFragment; - -public class CustomCreateOpenChannelFragment extends CreateOpenChannelFragment { - @Override - protected void onNewChannelCreated(@NonNull OpenChannel channel) { - if (isFragmentAlive() && getActivity() != null) { - startActivity(OpenChannelActivity.newIntent(requireContext(), CommunityActivity.class, channel.getUrl())); - getActivity().setResult(RESULT_OK); - shouldActivityFinish(); - } - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/CustomOpenChannelMessageListAdapter.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/CustomOpenChannelMessageListAdapter.java deleted file mode 100644 index 09ee6522..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/CustomOpenChannelMessageListAdapter.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.sendbird.uikit.customsample.openchannel; - -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; - -import com.sendbird.android.channel.OpenChannel; -import com.sendbird.android.message.BaseMessage; -import com.sendbird.android.message.UserMessage; -import com.sendbird.uikit.activities.adapter.OpenChannelMessageListAdapter; -import com.sendbird.uikit.activities.viewholder.MessageViewHolder; -import com.sendbird.uikit.customsample.consts.StringSet; -import com.sendbird.uikit.customsample.databinding.ViewOpenChannelHighlightMessageHolderBinding; -import com.sendbird.uikit.utils.MessageUtils; - -/** - * Implements the customized OpenChannelMessageListAdapter to adapt the customized message items. - */ -public class CustomOpenChannelMessageListAdapter extends OpenChannelMessageListAdapter { - - public static final int VIEW_MAP_MESSAGE_ME_TYPE = 1001; - public static final int VIEW_MAP_MESSAGE_OTHER_TYPE = 1002; - - public CustomOpenChannelMessageListAdapter(@NonNull OpenChannel channel, boolean useMessageGroupUI) { - super(channel, useMessageGroupUI); - } - - @NonNull - @Override - public MessageViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - // TODO: Create the custom ViewHolder and return it. - // Create your custom ViewHolder or call super.onCreateViewHolder() if you want to use the default. - if (viewType == VIEW_MAP_MESSAGE_ME_TYPE || viewType == VIEW_MAP_MESSAGE_OTHER_TYPE) { - LayoutInflater inflater = LayoutInflater.from(parent.getContext()); - return new HighlightOpenChannelMessageViewHolder(ViewOpenChannelHighlightMessageHolderBinding.inflate(inflater, parent, false)); - } else { - return super.onCreateViewHolder(parent, viewType); - } - } - - @Override - public void onBindViewHolder(@NonNull MessageViewHolder holder, int position) { - // You must call the super. You can use methods that MessageViewHolder provides - super.onBindViewHolder(holder, position); - // TODO: Bind the custom ViewHolder - } - - @Override - public int getItemViewType(int position) { - BaseMessage message = getItem(position); - - String customType = message.getCustomType(); - - if (!TextUtils.isEmpty(customType) && - customType.equals(StringSet.highlight) && - message instanceof UserMessage) { - if (MessageUtils.isMine(message)) { - return VIEW_MAP_MESSAGE_ME_TYPE; - } else { - return VIEW_MAP_MESSAGE_OTHER_TYPE; - } - } - - return super.getItemViewType(position); - } -} - diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/CustomOpenChannelSettingsFragment.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/CustomOpenChannelSettingsFragment.java deleted file mode 100644 index 0fdfd6d6..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/CustomOpenChannelSettingsFragment.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.sendbird.uikit.customsample.openchannel; - -import android.os.Bundle; - -import androidx.annotation.NonNull; - -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.fragments.OpenChannelSettingsFragment; -import com.sendbird.uikit.modules.OpenChannelSettingsModule; - -/** - * Implements the customized OpenChannelSettingsFragment - */ -public class CustomOpenChannelSettingsFragment extends OpenChannelSettingsFragment { - @NonNull - @Override - protected OpenChannelSettingsModule onCreateModule(@NonNull Bundle args) { - return new OpenChannelSettingsModule(requireContext(), new OpenChannelSettingsModule.Params(requireContext(), R.style.AppThemeCustom_Sendbird)); - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/HighlightOpenChannelMessageViewHolder.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/HighlightOpenChannelMessageViewHolder.java deleted file mode 100644 index f2cc46b0..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/HighlightOpenChannelMessageViewHolder.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.sendbird.uikit.customsample.openchannel; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.drawable.Drawable; -import android.text.TextUtils; -import android.text.format.DateUtils; -import android.view.View; - -import androidx.annotation.NonNull; - -import com.bumptech.glide.Glide; -import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.bumptech.glide.request.RequestOptions; -import com.sendbird.android.channel.BaseChannel; -import com.sendbird.android.channel.OpenChannel; -import com.sendbird.android.message.BaseMessage; -import com.sendbird.android.user.Sender; -import com.sendbird.uikit.activities.viewholder.MessageViewHolder; -import com.sendbird.uikit.consts.ClickableViewIdentifier; -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.customsample.databinding.ViewOpenChannelHighlightMessageHolderBinding; -import com.sendbird.uikit.customsample.utils.DrawableUtils; -import com.sendbird.uikit.model.MessageListUIParams; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * ViewHolder to draw the highlight message for OpenChannel. - */ -public class HighlightOpenChannelMessageViewHolder extends MessageViewHolder { - private final ViewOpenChannelHighlightMessageHolderBinding binding; - private final int operatorAppearance; - private final int nicknameAppearance; - - public HighlightOpenChannelMessageViewHolder(@NonNull ViewOpenChannelHighlightMessageHolderBinding binding) { - super(binding.getRoot()); - this.binding = binding; - TypedArray a = binding.getRoot().getContext().getTheme().obtainStyledAttributes(null, com.sendbird.uikit.R.styleable.MessageView, 0, 0); - try { - nicknameAppearance = a.getResourceId(com.sendbird.uikit.R.styleable.MessageView_sb_message_sender_name_text_appearance, com.sendbird.uikit.R.style.SendbirdCaption1OnLight02); - operatorAppearance = a.getResourceId(com.sendbird.uikit.R.styleable.MessageView_sb_message_operator_name_text_appearance, com.sendbird.uikit.R.style.SendbirdCaption1Secondary300); - } finally { - a.recycle(); - } - } - - @Override - public void bind(@NonNull BaseChannel channel, @NonNull BaseMessage message, @NonNull MessageListUIParams params) { - OpenChannel openChannel; - if (channel instanceof OpenChannel) { - openChannel = (OpenChannel) channel; - } else { - return; - } - - Context context = binding.getRoot().getContext(); - DrawableUtils.drawStatus(binding.ivStatus, message); - - binding.ivProfileView.setVisibility(View.VISIBLE); - binding.tvNickname.setVisibility(View.VISIBLE); - binding.tvSentAt.setVisibility(View.VISIBLE); - String sentAt = DateUtils.formatDateTime(context, message.getCreatedAt(), DateUtils.FORMAT_SHOW_TIME); - binding.tvSentAt.setText(sentAt); - - if (openChannel.isOperator(message.getSender())) { - binding.tvNickname.setTextAppearance(context, operatorAppearance); - } else { - binding.tvNickname.setTextAppearance(context, nicknameAppearance); - } - - Sender sender = message.getSender(); - String nickname = sender == null || TextUtils.isEmpty(sender.getNickname()) ? - context.getString(R.string.sb_text_channel_list_title_unknown) : - sender.getNickname(); - binding.tvNickname.setText(nickname); - - String url = ""; - if (sender != null && !TextUtils.isEmpty(sender.getProfileUrl())) { - url = sender.getProfileUrl(); - } - - Drawable errorIcon = DrawableUtils.createOvalIcon(binding.getRoot().getContext(), - R.color.background_300, R.drawable.icon_user, R.color.ondark_01); - Glide.with(context) - .load(url) - .diskCacheStrategy(DiskCacheStrategy.ALL) - .error(errorIcon) - .apply(RequestOptions.circleCropTransform()) - .into(binding.ivProfileView); - - binding.tvMessage.setText(message.getMessage()); - } - - @NonNull - @Override - public Map getClickableViewMap() { - return new ConcurrentHashMap() {{ - put(ClickableViewIdentifier.Chat.name(), binding.contentPanel); - put(ClickableViewIdentifier.Profile.name(), binding.ivProfileView); - }}; - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/OpenChannelMainActivity.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/OpenChannelMainActivity.java deleted file mode 100644 index 4a3a9476..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/OpenChannelMainActivity.java +++ /dev/null @@ -1,173 +0,0 @@ -package com.sendbird.uikit.customsample.openchannel; - -import android.os.Bundle; -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentActivity; -import androidx.fragment.app.FragmentManager; -import androidx.lifecycle.Lifecycle; -import androidx.viewpager2.adapter.FragmentStateAdapter; -import androidx.viewpager2.widget.ViewPager2; - -import com.google.android.material.tabs.TabLayoutMediator; -import com.sendbird.android.params.OpenChannelListQueryParams; -import com.sendbird.uikit.SendbirdUIKit; -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.customsample.SettingsFragment; -import com.sendbird.uikit.customsample.consts.StringSet; -import com.sendbird.uikit.customsample.databinding.ActivityOpenChannelMainBinding; -import com.sendbird.uikit.customsample.openchannel.community.CommunityListFragment; -import com.sendbird.uikit.customsample.openchannel.livestream.LiveStreamListAdapter; -import com.sendbird.uikit.customsample.openchannel.livestream.LiveStreamListFragment; -import com.sendbird.uikit.customsample.widgets.CustomTabView; -import com.sendbird.uikit.fragments.OpenChannelListFragment; - -/** - * Displays an open channel list screen. - */ -public class OpenChannelMainActivity extends AppCompatActivity { - private ActivityOpenChannelMainBinding binding; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - int themeResId = SendbirdUIKit.getDefaultThemeMode().getResId(); - setTheme(themeResId); - binding = ActivityOpenChannelMainBinding.inflate(getLayoutInflater()); - View view = binding.getRoot(); - setContentView(view); - initPage(); - } - - private void initPage() { - setSupportActionBar(binding.tbMain); - binding.vpMain.setAdapter(new MainAdapter(this)); - - int backgroundRedId = R.color.background_50; - binding.tlMain.setBackgroundResource(backgroundRedId); - - CustomTabView liveStreamTab = new CustomTabView(this); - liveStreamTab.setBadgeVisibility(View.GONE); - liveStreamTab.setTitle(getString(R.string.text_live_streams)); - liveStreamTab.setIcon(R.drawable.icon_streaming); - - CustomTabView communityTab = new CustomTabView(this); - communityTab.setBadgeVisibility(View.GONE); - communityTab.setTitle(getString(R.string.text_community)); - communityTab.setIcon(R.drawable.icon_channels); - - CustomTabView settingsTab = new CustomTabView(this); - settingsTab.setBadgeVisibility(View.GONE); - settingsTab.setTitle(getString(R.string.text_tab_settings)); - settingsTab.setIcon(R.drawable.icon_settings_filled); - - binding.tvDescription.setVisibility(View.VISIBLE); - binding.tvDescription.setText(R.string.text_live_streaming_description); - setActionBarTitle(getString(R.string.text_live_streams)); - - new TabLayoutMediator(binding.tlMain, binding.vpMain, (tab, position) -> { - switch (position) { - case 0: - tab.setCustomView(liveStreamTab); - break; - case 1: - tab.setCustomView(communityTab); - break; - case 2: - tab.setCustomView(settingsTab); - break; - default: - break; - - } - }).attach(); - - binding.vpMain.setOffscreenPageLimit(3); - binding.vpMain.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { - @Override - public void onPageSelected(int position) { - if (position == 0) { - binding.tvDescription.setVisibility(View.VISIBLE); - binding.tvDescription.setText(R.string.text_live_streaming_description); - setActionBarTitle(getString(R.string.text_live_streams)); - binding.tbMain.setVisibility(View.VISIBLE); - binding.tbBorder.setVisibility(View.VISIBLE); - } else if (position == 1) { - binding.tvDescription.setVisibility(View.GONE); - setActionBarTitle(getString(R.string.text_community)); - binding.tbMain.setVisibility(View.VISIBLE); - binding.tbBorder.setVisibility(View.VISIBLE); - } else { - binding.tvDescription.setVisibility(View.GONE); - setActionBarTitle(getString(R.string.text_tab_settings)); - binding.tbMain.setVisibility(View.GONE); - binding.tbBorder.setVisibility(View.GONE); - } - } - }); - } - - private void setActionBarTitle(String title) { - if (getSupportActionBar() == null) return; - getSupportActionBar().setTitle(title); - } - - private static class MainAdapter extends FragmentStateAdapter { - private static final int PAGE_SIZE = 3; - - /** - * @param fragmentActivity if the {@link ViewPager2} lives directly in a - * {@link FragmentActivity} subclass. - * @see FragmentStateAdapter#FragmentStateAdapter(Fragment) - * @see FragmentStateAdapter#FragmentStateAdapter(FragmentManager, Lifecycle) - */ - public MainAdapter(@NonNull FragmentActivity fragmentActivity) { - super(fragmentActivity); - } - - @NonNull - @Override - public Fragment createFragment(int position) { - - if (position == 0) { - final OpenChannelListQueryParams params = new OpenChannelListQueryParams(); - params.setCustomTypeFilter(StringSet.SB_LIVE_TYPE); - return new com.sendbird.uikit.fragments.OpenChannelListFragment.Builder() - .setCustomFragment(new LiveStreamListFragment()) - .setOpenChannelListAdapter(new LiveStreamListAdapter()) - .setUseHeader(false) - .setUseRefreshLayout(false) - .setCustomQueryParams(params) - .build(); - } else if (position == 1) { - final OpenChannelListQueryParams params = new OpenChannelListQueryParams(); - params.setCustomTypeFilter(StringSet.SB_COMMUNITY_TYPE); - return new OpenChannelListFragment.Builder() - .setCustomFragment(new CommunityListFragment()) - .setUseHeader(false) - .setCustomQueryParams(params) - .build(); - } else { - SettingsFragment fragment = new SettingsFragment(); - Bundle bundle = new Bundle(); - bundle.putBoolean(StringSet.SETTINGS_USE_DO_NOT_DISTURB, false); - fragment.setArguments(bundle); - return fragment; - } - } - - /** - * Returns the total number of items in the data set held by the adapter. - * - * @return The total number of items in this adapter. - */ - @Override - public int getItemCount() { - return PAGE_SIZE; - } - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/community/CommunityActivity.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/community/CommunityActivity.java deleted file mode 100644 index 2b4d87ac..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/community/CommunityActivity.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.sendbird.uikit.customsample.openchannel.community; - -import android.content.Intent; -import android.os.Bundle; - -import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; - -import com.sendbird.uikit.activities.OpenChannelActivity; - - -/** - * Displays an open channel screen used for community. - */ -public class CommunityActivity extends OpenChannelActivity { - - @NonNull - @Override - protected Fragment createFragment() { - final Intent intent = getIntent(); - final Bundle args = intent != null && intent.getExtras() != null ? intent.getExtras() : new Bundle(); - CommunityChannelFragment fragment = new CommunityChannelFragment(); - fragment.setArguments(args); - return fragment; - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/community/CommunityChannelFragment.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/community/CommunityChannelFragment.java deleted file mode 100644 index 0a3cf124..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/community/CommunityChannelFragment.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.sendbird.uikit.customsample.openchannel.community; - -import android.os.Bundle; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; - -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.customsample.models.CustomMessageType; -import com.sendbird.uikit.customsample.openchannel.CustomOpenChannelMessageListAdapter; -import com.sendbird.uikit.fragments.OpenChannelFragment; -import com.sendbird.uikit.model.ReadyStatus; -import com.sendbird.uikit.modules.OpenChannelModule; -import com.sendbird.uikit.modules.components.OpenChannelHeaderComponent; -import com.sendbird.uikit.vm.OpenChannelViewModel; - -/** - * Displays an open channel screen used for community. - */ -public class CommunityChannelFragment extends OpenChannelFragment { - private CustomMessageType customMessageType = CustomMessageType.NONE; - - @NonNull - @Override - protected OpenChannelModule onCreateModule(@NonNull Bundle args) { - return new OpenChannelModule(requireContext(), new OpenChannelModule.Params(requireContext(), R.style.AppThemeCustom_Sendbird)); - } - - @Override - protected void onConfigureParams(@NonNull OpenChannelModule module, @NonNull Bundle args) { - super.onConfigureParams(module, args); - OpenChannelModule.Params params = module.getParams(); - params.setUseHeader(true); - - OpenChannelHeaderComponent.Params headerParams = module.getHeaderComponent().getParams(); - headerParams.setUseLeftButton(true); - } - - @Override - protected void onBeforeReady(@NonNull ReadyStatus status, @NonNull OpenChannelModule module, @NonNull OpenChannelViewModel viewModel) { - super.onBeforeReady(status, module, viewModel); - getModule().getMessageInputComponent().setOnInputLeftButtonClickListener(v -> showMessageTypeDialog()); - if (viewModel.getChannel() != null) { - getModule().getMessageListComponent().setAdapter(new CustomOpenChannelMessageListAdapter(viewModel.getChannel(), true)); - } - } - - public void setCustomMessageType(@NonNull CustomMessageType customMessageType) { - this.customMessageType = customMessageType; - } - - @NonNull - public CustomMessageType getCustomMessageType() { - return customMessageType; - } - - private void showMessageTypeDialog() { - if (!isFragmentAlive()) return; - AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); - builder.setTitle("Pick message type") - .setMultiChoiceItems(new String[]{com.sendbird.uikit.customsample.consts.StringSet.highlight}, - new boolean[]{getCustomMessageType().equals(CustomMessageType.HIGHLIGHT)}, - (dialog, which, isChecked) -> { - final CustomMessageType type = isChecked ? CustomMessageType.HIGHLIGHT : CustomMessageType.NONE; - setCustomMessageType(type); - }) - .create() - .show(); - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/community/CommunityListFragment.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/community/CommunityListFragment.java deleted file mode 100644 index 766e260c..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/community/CommunityListFragment.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.sendbird.uikit.customsample.openchannel.community; - -import android.content.Intent; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; - -import androidx.annotation.NonNull; - -import com.sendbird.android.channel.OpenChannel; -import com.sendbird.uikit.activities.CreateOpenChannelActivity; -import com.sendbird.uikit.activities.OpenChannelActivity; -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.fragments.OpenChannelListFragment; -import com.sendbird.uikit.log.Logger; - -/** - * Displays an open channel list screen used for community. - */ -public class CommunityListFragment extends OpenChannelListFragment { - @Override - public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - inflater.inflate(R.menu.community_list_menu, menu); - } - - @Override - public void onPrepareOptionsMenu(@NonNull Menu menu) { - final MenuItem createMenuItem = menu.findItem(R.id.action_create_channel); - View rootView = createMenuItem.getActionView(); - if (rootView != null) rootView.setOnClickListener(v -> onOptionsItemSelected(createMenuItem)); - super.onPrepareOptionsMenu(menu); - } - - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - if (item.getItemId() == R.id.action_create_channel && getActivity() != null) { - Logger.d("++ create button clicked"); - Intent intent = new Intent(getActivity(), CreateOpenChannelActivity.class); - startActivity(intent); - } - return super.onOptionsItemSelected(item); - } - - @Override - protected void onItemClicked(@NonNull View view, int position, @NonNull OpenChannel channel) { - startActivity(OpenChannelActivity.newIntent(requireContext(), CommunityActivity.class, channel.getUrl())); - - } - - @Override - public void onResume() { - super.onResume(); - setHasOptionsMenu(true); - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/livestream/LiveStreamActivity.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/livestream/LiveStreamActivity.java deleted file mode 100644 index a41c56ac..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/livestream/LiveStreamActivity.java +++ /dev/null @@ -1,271 +0,0 @@ -package com.sendbird.uikit.customsample.openchannel.livestream; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.content.Context; -import android.content.Intent; -import android.content.res.Configuration; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.text.TextUtils; -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import androidx.fragment.app.FragmentManager; - -import com.bumptech.glide.Glide; -import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.sendbird.android.SendbirdChat; -import com.sendbird.android.channel.BaseChannel; -import com.sendbird.android.channel.OpenChannel; -import com.sendbird.android.handler.OpenChannelHandler; -import com.sendbird.android.message.BaseMessage; -import com.sendbird.android.user.User; -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.customsample.consts.StringSet; -import com.sendbird.uikit.customsample.databinding.ActivityLiveStreamBinding; -import com.sendbird.uikit.customsample.models.LiveStreamingChannelData; -import com.sendbird.uikit.fragments.OpenChannelFragment; -import com.sendbird.uikit.utils.ContextUtils; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.lang.ref.WeakReference; -import java.util.Locale; - -/** - * Displays an open channel screen used for live stream. - */ -public class LiveStreamActivity extends AppCompatActivity { - private final String CHANNEL_HANDLER_KEY = getClass().getSimpleName() + System.currentTimeMillis(); - - private ActivityLiveStreamBinding binding; - private String creatorName; - private String channelUrl; - private String inputText; - - private final HideHandler hideHandler = new HideHandler(this); - - /** - * Hides the system UI for full screen. - */ - private static class HideHandler extends Handler { - private final WeakReference weakReference; - - public HideHandler(@NonNull LiveStreamActivity activity) { - super(Looper.getMainLooper()); - this.weakReference = new WeakReference<>(activity); - } - - @Override - public void handleMessage(@NonNull Message msg) { - LiveStreamActivity activity = weakReference.get(); - if (activity != null) { - activity.hideSystemUI(); - } - } - } - - @NonNull - public static Intent newIntent(@NonNull Context context, @NonNull String channelUrl) { - Intent intent = new Intent(context, LiveStreamActivity.class); - intent.putExtra(StringSet.KEY_CHANNEL_URL, channelUrl); - return intent; - } - - @Override - protected void onSaveInstanceState(@NonNull Bundle outState) { - super.onSaveInstanceState(outState); - outState.putString(StringSet.KEY_INPUT_TEXT, inputText); - } - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - binding = ActivityLiveStreamBinding.inflate(getLayoutInflater()); - View view = binding.getRoot(); - setContentView(view); - addChannelHandler(); - binding.ivLive.setVisibility(View.VISIBLE); - binding.ivLive.setOnClickListener(v -> { - if (binding.groupLiveControl.getVisibility() == View.VISIBLE) { - binding.groupLiveControl.setVisibility(View.GONE); - } else { - binding.groupLiveControl.setVisibility(View.VISIBLE); - } - }); - - binding.ivClose.setOnClickListener(v -> finish()); - if (binding.ivChatToggle != null) { - binding.ivChatToggle.setOnClickListener(v -> { - if (binding.sbFragmentContainer.getVisibility() == View.GONE) { - binding.sbFragmentContainer.animate() - .setDuration(300) - .alpha(1.0f) - .translationX(0.0f) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - super.onAnimationStart(animation); - binding.sbFragmentContainer.setVisibility(View.VISIBLE); - } - }); - binding.ivChatToggle.animate() - .setDuration(300) - .translationX(0.0f); - binding.ivChatToggle.setImageResource(R.drawable.ic_chat_hide); - } else { - binding.sbFragmentContainer.animate() - .setDuration(300) - .alpha(0.0f) - .translationX(binding.sbFragmentContainer.getWidth()) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); - binding.sbFragmentContainer.setVisibility(View.GONE); - } - }); - binding.ivChatToggle.animate() - .setDuration(300) - .translationX(binding.sbFragmentContainer.getWidth()); - binding.ivChatToggle.setImageResource(R.drawable.ic_chat_show); - } - }); - } - - channelUrl = getIntent().getStringExtra(StringSet.KEY_CHANNEL_URL); - if (TextUtils.isEmpty(channelUrl)) { - ContextUtils.toastError(this, R.string.sb_text_error_get_channel); - } else { - OpenChannel.getChannel(channelUrl, (openChannel, e) -> { - if (e != null) { - return; - } - - if (binding == null || openChannel == null) return; - updateParticipantCount(openChannel.getParticipantCount()); - try { - LiveStreamingChannelData channelData = new LiveStreamingChannelData(new JSONObject(openChannel.getData())); - if (channelData.getCreator() == null) { - creatorName = ""; - } else { - creatorName = channelData.getCreator().getNickname(); - } - Glide.with(binding.getRoot().getContext()) - .load(channelData.getLiveUrl()) - .override(binding.ivLive.getMeasuredWidth(), binding.ivLive.getHeight()) - .diskCacheStrategy(DiskCacheStrategy.ALL) - .error(R.color.background_600) - .into(binding.ivLive); - } catch (JSONException ex) { - ex.printStackTrace(); - } - - if(savedInstanceState != null) { - inputText = savedInstanceState.getString(StringSet.KEY_INPUT_TEXT); - savedInstanceState.clear(); - } - - OpenChannelFragment fragment = createOpenChannelFragment(openChannel.getUrl()); - FragmentManager manager = getSupportFragmentManager(); - manager.popBackStack(); - manager.beginTransaction() - .replace(R.id.sb_fragment_container, fragment) - .commit(); - }); - } - } - - private void addChannelHandler() { - SendbirdChat.addChannelHandler(CHANNEL_HANDLER_KEY, new OpenChannelHandler() { - @Override - public void onMessageReceived(@NonNull BaseChannel baseChannel, @NonNull BaseMessage baseMessage) { - - } - - @Override - public void onUserEntered(@NonNull OpenChannel channel, @NonNull User user) { - if (channel.getUrl().equals(channelUrl)) { - updateParticipantCount(channel.getParticipantCount()); - } - } - - @Override - public void onUserExited(@NonNull OpenChannel channel, @NonNull User user) { - if (channel.getUrl().equals(channelUrl)) { - updateParticipantCount(channel.getParticipantCount()); - } - } - }); - } - - private void updateParticipantCount(int count) { - String text = String.valueOf(count); - if (count > 1000) { - text = String.format(Locale.US, "%.1fK", count / 1000F); - } - binding.tvParticipantCount.setText(String.format(getString(R.string.text_participants), text)); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - hideHandler.removeMessages(0); - SendbirdChat.removeChannelHandler(CHANNEL_HANDLER_KEY); - } - - @Override - public void onWindowFocusChanged(boolean hasFocus) { - super.onWindowFocusChanged(hasFocus); - if (hasFocus) { - delayedHide(); - } else { - hideHandler.removeMessages(0); - } - } - - private void hideSystemUI() { - if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { - getWindow().getDecorView().setSystemUiVisibility( - View.SYSTEM_UI_FLAG_LAYOUT_STABLE | - View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | - View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | - View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | - View.SYSTEM_UI_FLAG_FULLSCREEN | - View.SYSTEM_UI_FLAG_LOW_PROFILE | - View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY - ); - } - } - - private void delayedHide() { - hideHandler.removeMessages(0); - hideHandler.sendEmptyMessageDelayed(0, 300); - } - - /** - * Creates OpenChannelFragment with channel url. - *

- * In preparation for screen configuration change, the value is initialized. - *

- * @param channelUrl The channel url to be applied to this screen - * @return OpenChannelFragment instance - */ - @NonNull - protected OpenChannelFragment createOpenChannelFragment(@NonNull String channelUrl) { - final Bundle args = new Bundle(); - args.putString("CHANNEL_URL", channelUrl); - args.putString("DESCRIPTION", creatorName); - args.putString("INPUT_TEXT", inputText); - - LiveStreamChannelFragment fragment = new LiveStreamChannelFragment(); - fragment.setArguments(args); - return fragment; - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/livestream/LiveStreamChannelFragment.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/livestream/LiveStreamChannelFragment.java deleted file mode 100644 index 769da6b6..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/livestream/LiveStreamChannelFragment.java +++ /dev/null @@ -1,155 +0,0 @@ -package com.sendbird.uikit.customsample.openchannel.livestream; - -import android.content.res.Configuration; -import android.net.Uri; -import android.os.Bundle; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; - -import com.sendbird.android.message.BaseMessage; -import com.sendbird.android.params.FileMessageCreateParams; -import com.sendbird.android.params.UserMessageCreateParams; -import com.sendbird.android.params.UserMessageUpdateParams; -import com.sendbird.uikit.consts.KeyboardDisplayType; -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.customsample.models.CustomMessageType; -import com.sendbird.uikit.customsample.openchannel.CustomOpenChannelMessageListAdapter; -import com.sendbird.uikit.fragments.OpenChannelFragment; -import com.sendbird.uikit.model.ReadyStatus; -import com.sendbird.uikit.modules.OpenChannelModule; -import com.sendbird.uikit.modules.components.OpenChannelHeaderComponent; -import com.sendbird.uikit.modules.components.OpenChannelMessageInputComponent; -import com.sendbird.uikit.vm.OpenChannelViewModel; - -/** - * Displays an open channel screen used for live stream. - */ -public class LiveStreamChannelFragment extends OpenChannelFragment { - private String inputText; - - @NonNull - @Override - protected OpenChannelModule onCreateModule(@NonNull Bundle args) { - return new OpenChannelModule(requireContext(), new OpenChannelModule.Params(requireContext(), R.style.AppThemeCustom_Sendbird)); - } - - @Override - protected void onConfigureParams(@NonNull OpenChannelModule module, @NonNull Bundle args) { - super.onConfigureParams(module, args); - final String creatorName = args.getString("DESCRIPTION"); - this.inputText = args.getString("INPUT_TEXT"); - - OpenChannelModule.Params moduleParams = module.getParams(); - moduleParams.setUseOverlayMode(getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE); - moduleParams.setUseHeader(true); - - OpenChannelHeaderComponent.Params headerParams = module.getHeaderComponent().getParams(); - headerParams.setDescription(creatorName); - - OpenChannelMessageInputComponent.Params inputParams = module.getMessageInputComponent().getParams(); - inputParams.setInputText(inputText); - inputParams.setKeyboardDisplayType(KeyboardDisplayType.Dialog); - } - - @Override - protected void onBeforeReady(@NonNull ReadyStatus status, @NonNull OpenChannelModule module, @NonNull OpenChannelViewModel viewModel) { - super.onBeforeReady(status, module, viewModel); - getModule().getMessageInputComponent().setOnInputTextChangedListener((s, start, before, count) -> inputText = s.toString()); - getModule().getMessageInputComponent().setOnInputLeftButtonClickListener(v -> showMessageTypeDialog()); - if (viewModel.getChannel() != null) { - getModule().getMessageListComponent().setAdapter(new CustomOpenChannelMessageListAdapter(viewModel.getChannel(), true)); - } - } - - private CustomMessageType customMessageType = CustomMessageType.NONE; - - @Override - protected void onBeforeSendUserMessage(@NonNull UserMessageCreateParams params) { - super.onBeforeSendUserMessage(params); - params.setCustomType(customMessageType.getValue()); - params.setData(null); - params.setMentionedUserIds(null); - params.setMentionedUsers(null); - params.setMetaArrays(null); - params.setParentMessageId(0); - params.setPushNotificationDeliveryOption(null); - params.setTranslationTargetLanguages(null); - } - - @Override - protected void onBeforeSendFileMessage(@NonNull FileMessageCreateParams params) { - super.onBeforeSendFileMessage(params); - params.setCustomType(customMessageType.getValue()); - params.setData(null); - params.setMentionedUserIds(null); - params.setMentionedUsers(null); - params.setMetaArrays(null); - params.setParentMessageId(0); - params.setPushNotificationDeliveryOption(null); - } - - @Override - protected void onBeforeUpdateUserMessage(@NonNull UserMessageUpdateParams params) { - super.onBeforeUpdateUserMessage(params); - params.setCustomType(customMessageType.getValue()); - params.setData(null); - params.setMentionedUserIds(null); - params.setMentionedUsers(null); - } - - @Override - protected void sendUserMessage(@NonNull UserMessageCreateParams params) { - super.sendUserMessage(params); - } - - @Override - protected void sendFileMessage(@NonNull Uri uri) { - super.sendFileMessage(uri); - } - - @Override - protected void updateUserMessage(long messageId, @NonNull UserMessageUpdateParams params) { - super.updateUserMessage(messageId, params); - } - - @Override - protected void deleteMessage(@NonNull BaseMessage message) { - super.deleteMessage(message); - } - - @Override - protected void resendMessage(@NonNull BaseMessage message) { - super.resendMessage(message); - } - - public void setCustomMessageType(@NonNull CustomMessageType customMessageType) { - this.customMessageType = customMessageType; - } - - @NonNull - public CustomMessageType getCustomMessageType() { - return customMessageType; - } - - private void showMessageTypeDialog() { - if (!isFragmentAlive()) return; - AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); - builder.setTitle("Pick message type") - .setMultiChoiceItems(new String[]{com.sendbird.uikit.customsample.consts.StringSet.highlight}, - new boolean[]{getCustomMessageType().equals(CustomMessageType.HIGHLIGHT)}, - (dialog, which, isChecked) -> { - final CustomMessageType type = isChecked ? CustomMessageType.HIGHLIGHT : CustomMessageType.NONE; - setCustomMessageType(type); - }) - .create() - .show(); - } - - @NonNull - @Override - protected String getChannelUrl() { - final Bundle args = getArguments() == null ? new Bundle() : getArguments(); - return args.getString("CHANNEL_URL", ""); - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/livestream/LiveStreamListAdapter.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/livestream/LiveStreamListAdapter.java deleted file mode 100644 index 71e59543..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/livestream/LiveStreamListAdapter.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.sendbird.uikit.customsample.openchannel.livestream; - -import android.graphics.drawable.Drawable; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; - -import com.bumptech.glide.Glide; -import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.sendbird.android.channel.OpenChannel; -import com.sendbird.uikit.activities.adapter.OpenChannelListAdapter; -import com.sendbird.uikit.activities.viewholder.BaseViewHolder; -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.customsample.databinding.ViewLiveStreamListItemBinding; -import com.sendbird.uikit.customsample.models.LiveStreamingChannelData; -import com.sendbird.uikit.customsample.utils.DrawableUtils; -import com.sendbird.uikit.interfaces.UserInfo; -import com.sendbird.uikit.utils.TextUtils; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.Locale; - -/** - * RecyclerView adapter for OpenChannel list used for live stream. - */ -public class LiveStreamListAdapter extends OpenChannelListAdapter { - @NonNull - @Override - public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - LayoutInflater inflater = LayoutInflater.from(parent.getContext()); - ViewLiveStreamListItemBinding binding = ViewLiveStreamListItemBinding.inflate(inflater, parent, false); - return new LiveStreamingListViewHolder(binding); - } - - static class LiveStreamingListViewHolder extends BaseViewHolder { - private final ViewLiveStreamListItemBinding binding; - - public LiveStreamingListViewHolder(@NonNull ViewLiveStreamListItemBinding binding) { - super(binding.getRoot()); - this.binding = binding; - } - - @Override - public void bind(@NonNull OpenChannel openChannel) { - int count = openChannel.getParticipantCount(); - String text = String.valueOf(count); - if (count > 1000) { - text = String.format(Locale.US, "%.1fK", count / 1000F); - } - binding.tvParticipantCount.setText(text); - - try { - LiveStreamingChannelData channelData = new LiveStreamingChannelData(new JSONObject(openChannel.getData())); - - binding.tvLiveTitle.setVisibility(View.VISIBLE); - binding.tvLiveTitle.setText(channelData.getName()); - - UserInfo creatorInfo = channelData.getCreator(); - if (creatorInfo == null || TextUtils.isEmpty(creatorInfo.getNickname())) { - binding.tvCreator.setVisibility(View.GONE); - } else { - binding.tvCreator.setVisibility(View.VISIBLE); - binding.tvCreator.setText(creatorInfo.getNickname()); - } - - if (channelData.getTags() == null || TextUtils.isEmpty(channelData.getTags().get(0))) { - binding.tvBadge.setVisibility(View.GONE); - } else { - binding.tvBadge.setVisibility(View.VISIBLE); - binding.tvBadge.setText(channelData.getTags().get(0)); - } - - Glide.with(binding.getRoot().getContext()) - .load(channelData.getLiveUrl()) - .override(binding.ivLiveThumbnail.getWidth(), binding.ivLiveThumbnail.getHeight()) - .centerCrop() - .diskCacheStrategy(DiskCacheStrategy.ALL) - .error(R.color.background_600) - .into(binding.ivLiveThumbnail); - - binding.ivChannelThumbnail.setVisibility(View.VISIBLE); - - Drawable errorIcon = DrawableUtils.createOvalIcon(binding.getRoot().getContext(), - R.color.background_300, R.drawable.icon_channels, R.color.ondark_01); - Glide.with(binding.getRoot().getContext()) - .load(channelData.getThumbnailUrl()) - .override(binding.ivChannelThumbnail.getWidth(), binding.ivChannelThumbnail.getHeight()) - .circleCrop() - .diskCacheStrategy(DiskCacheStrategy.ALL) - .error(errorIcon) - .into(binding.ivChannelThumbnail); - } catch (JSONException e) { - e.printStackTrace(); - binding.ivLiveThumbnail.setImageDrawable(null); - binding.ivChannelThumbnail.setVisibility(View.GONE); - binding.tvLiveTitle.setVisibility(View.GONE); - binding.tvBadge.setVisibility(View.GONE); - binding.tvCreator.setVisibility(View.GONE); - } - } - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/livestream/LiveStreamListFragment.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/livestream/LiveStreamListFragment.java deleted file mode 100644 index db3954d8..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/livestream/LiveStreamListFragment.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.sendbird.uikit.customsample.openchannel.livestream; - -import android.view.View; - -import androidx.annotation.NonNull; - -import com.sendbird.android.channel.OpenChannel; -import com.sendbird.uikit.fragments.OpenChannelListFragment; - -/** - * Displays an open channel list screen used for live stream. - */ -public class LiveStreamListFragment extends OpenChannelListFragment { - @Override - protected void onItemClicked(@NonNull View view, int position, @NonNull OpenChannel channel) { - startActivity(LiveStreamActivity.newIntent(requireContext(), channel.getUrl())); - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/utils/DrawableUtils.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/utils/DrawableUtils.java deleted file mode 100644 index 994ee2f0..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/utils/DrawableUtils.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.sendbird.uikit.customsample.utils; - -import android.content.Context; -import android.content.res.ColorStateList; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.LayerDrawable; -import android.graphics.drawable.ShapeDrawable; -import android.graphics.drawable.shapes.OvalShape; -import android.widget.ImageView; - -import androidx.annotation.ColorRes; -import androidx.annotation.DrawableRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.content.res.AppCompatResources; -import androidx.core.graphics.drawable.DrawableCompat; - -import com.sendbird.android.message.BaseMessage; -import com.sendbird.android.message.SendingStatus; -import com.sendbird.uikit.R; - -/** - * This provides methods to draw icon and color. - */ -public class DrawableUtils { - @Nullable - public static Drawable setTintList(@NonNull Context context, int resId, int colorRes) { - if (colorRes == 0) { - return AppCompatResources.getDrawable(context, resId); - } - return setTintList(AppCompatResources.getDrawable(context, resId), AppCompatResources.getColorStateList(context, colorRes)); - } - - @Nullable - public static Drawable setTintList(@Nullable Drawable drawable, @Nullable ColorStateList colorStateList) { - if (drawable == null || colorStateList == null) { - return drawable; - } - drawable = DrawableCompat.wrap(drawable); - DrawableCompat.setTintList(drawable, colorStateList); - return drawable.mutate(); - } - - @NonNull - public static Drawable createOvalIcon(@NonNull Context context, @ColorRes int backgroundColor, - @DrawableRes int iconRes, @ColorRes int iconTint) { - return createOvalIcon(context, backgroundColor, 255, iconRes, iconTint); - } - - @NonNull - public static Drawable createOvalIcon(@NonNull Context context, @ColorRes int backgroundColor, int backgroundAlpha, - @DrawableRes int iconRes, @ColorRes int iconTint) { - ShapeDrawable ovalBackground = new ShapeDrawable(new OvalShape()); - ovalBackground.getPaint().setColor(context.getResources().getColor(backgroundColor)); - ovalBackground.getPaint().setAlpha(backgroundAlpha); - Drawable icon = setTintList(context, iconRes, iconTint); - int inset = (int) context.getResources().getDimension(R.dimen.sb_size_24); - return createLayerIcon(ovalBackground, icon, inset); - } - - @NonNull - public static Drawable createLayerIcon(@NonNull Drawable background, @Nullable Drawable icon, int inset) { - Drawable[] layer = {background, icon}; - LayerDrawable layerDrawable = new LayerDrawable(layer); - layerDrawable.setLayerInset(1, inset, inset, inset, inset); - return layerDrawable; - } - - public static void drawStatus(@NonNull ImageView view, @NonNull BaseMessage message) { - Context context = view.getContext(); - if (message.getSendingStatus() == SendingStatus.CANCELED || message.getSendingStatus() == SendingStatus.FAILED) { - view.setImageDrawable(DrawableUtils.setTintList(context, com.sendbird.uikit.customsample.R.drawable.icon_error, com.sendbird.uikit.customsample.R.color.error_300)); - } else if (message.getSendingStatus() == SendingStatus.SUCCEEDED) { - view.setImageDrawable(DrawableUtils.setTintList(context, com.sendbird.uikit.customsample.R.drawable.icon_done, com.sendbird.uikit.customsample.R.color.secondary_300)); - } else if (message.getSendingStatus() == SendingStatus.PENDING) { - view.setImageDrawable(DrawableUtils.setTintList(context, com.sendbird.uikit.customsample.R.drawable.sb_message_progress, com.sendbird.uikit.customsample.R.color.primary_300)); - } - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/utils/PreferenceUtils.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/utils/PreferenceUtils.java deleted file mode 100644 index be4c24d2..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/utils/PreferenceUtils.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.sendbird.uikit.customsample.utils; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.SharedPreferences; - -import androidx.annotation.NonNull; - -/** - * This provides methods to manage preferences data. - */ -public class PreferenceUtils { - - private static final String PREFERENCE_KEY_USER_ID = "PREFERENCE_KEY_USER_ID"; - private static final String PREFERENCE_KEY_NICKNAME = "PREFERENCE_KEY_NICKNAME"; - private static final String PREFERENCE_KEY_PROFILE_URL = "PREFERENCE_KEY_PROFILE_URL"; - private static final String PREFERENCE_KEY_DO_NOT_DISTURB = "PREFERENCE_KEY_DO_NOT_DISTURB"; - - @SuppressLint("StaticFieldLeak") - private static Context context; - - // Prevent instantiation - private PreferenceUtils() { - } - - public static void init(@NonNull Context appContext) { - context = appContext.getApplicationContext(); - } - - private static SharedPreferences getSharedPreferences() { - return context.getSharedPreferences("sendbird", Context.MODE_PRIVATE); - } - - public static void setUserId(@NonNull String userId) { - SharedPreferences.Editor editor = getSharedPreferences().edit(); - editor.putString(PREFERENCE_KEY_USER_ID, userId).apply(); - } - - @NonNull - public static String getUserId() { - final String result = getSharedPreferences().getString(PREFERENCE_KEY_USER_ID, ""); - return result == null ? "" : result; - } - - public static void setNickname(@NonNull String nickname) { - SharedPreferences.Editor editor = getSharedPreferences().edit(); - editor.putString(PREFERENCE_KEY_NICKNAME, nickname).apply(); - } - - @NonNull - public static String getNickname() { - final String result = getSharedPreferences().getString(PREFERENCE_KEY_NICKNAME, ""); - return result == null ? "" : result; - } - - public static void setProfileUrl(@NonNull String profileUrl) { - SharedPreferences.Editor editor = getSharedPreferences().edit(); - editor.putString(PREFERENCE_KEY_PROFILE_URL, profileUrl).apply(); - } - - @NonNull - public static String getProfileUrl() { - final String result = getSharedPreferences().getString(PREFERENCE_KEY_PROFILE_URL, ""); - return result == null ? "" : result; - } - - public static void setDoNotDisturb(boolean doNotDisturb) { - SharedPreferences.Editor editor = getSharedPreferences().edit(); - editor.putBoolean(PREFERENCE_KEY_DO_NOT_DISTURB, doNotDisturb).apply(); - } - - public static boolean getDoNotDisturb() { - return getSharedPreferences().getBoolean(PREFERENCE_KEY_DO_NOT_DISTURB, false); - } - - public static void clearAll() { - SharedPreferences.Editor editor = getSharedPreferences().edit(); - editor.clear().apply(); - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/utils/PushUtils.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/utils/PushUtils.java deleted file mode 100644 index fec86a0d..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/utils/PushUtils.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.sendbird.uikit.customsample.utils; - -import androidx.annotation.NonNull; - -import com.sendbird.android.handler.PushRequestCompleteHandler; -import com.sendbird.android.push.SendbirdPushHandler; -import com.sendbird.android.push.SendbirdPushHelper; - -/** - * This provides methods to manage push handler. - */ -public class PushUtils { - - public static void registerPushHandler(@NonNull SendbirdPushHandler handler) { - SendbirdPushHelper.registerPushHandler(handler); - } - - public static void unregisterPushHandler(@NonNull PushRequestCompleteHandler listener) { - SendbirdPushHelper.unregisterPushHandler(listener); - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/widgets/CustomTabView.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/widgets/CustomTabView.java deleted file mode 100644 index 2e295d9c..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/widgets/CustomTabView.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.sendbird.uikit.customsample.widgets; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.TextView; - -import androidx.annotation.DrawableRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.content.res.AppCompatResources; - -import com.sendbird.uikit.SendbirdUIKit; -import com.sendbird.uikit.customsample.R; -import com.sendbird.uikit.customsample.utils.DrawableUtils; - -/** - * View displaying icon and badge in tabs. - */ -public class CustomTabView extends FrameLayout { - private int tintColorRedId; - private TextView badgeView; - private ImageView iconView; - private TextView titleView; - - public CustomTabView(@NonNull Context context) { - this(context, null); - } - - public CustomTabView(@NonNull Context context, @Nullable AttributeSet attrs) { - this(context, attrs, 0); - } - - public CustomTabView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - initView(context); - } - - private void initView(@NonNull Context context) { - LayoutInflater inflater = LayoutInflater.from(getContext()); - View view = inflater.inflate(R.layout.view_custom_tab, this, true); - badgeView = view.findViewById(R.id.badge); - iconView = view.findViewById(R.id.ivIcon); - titleView = view.findViewById(R.id.tvTitle); - - boolean isDarkMode = SendbirdUIKit.isDarkMode(); - tintColorRedId = isDarkMode ? R.color.selector_tab_tint_dark : R.color.selector_tab_tint; - - int badgeTextAppearance = isDarkMode ? R.style.SendbirdCaption3OnLight01 : R.style.SendbirdCaption3OnDark01; - int badgeBackgroundRes = isDarkMode ? R.drawable.shape_badge_background_dark : R.drawable.shape_badge_background; - int titleTextAppearance = isDarkMode ? R.style.SendbirdCaption2Primary200 : R.style.SendbirdCaption2Primary300; - - badgeView.setTextAppearance(context, badgeTextAppearance); - badgeView.setBackgroundResource(badgeBackgroundRes); - titleView.setTextAppearance(context, titleTextAppearance); - titleView.setTextColor(AppCompatResources.getColorStateList(context, tintColorRedId)); - } - - public void setBadgeVisibility(int visibility) { - badgeView.setVisibility(visibility); - } - - public void setBadgeCount(@Nullable String countString) { - badgeView.setText(countString); - } - - public void setIcon(@DrawableRes int iconResId) { - iconView.setImageDrawable(DrawableUtils.setTintList(getContext(), iconResId, tintColorRedId)); - } - - public void setTitle(@Nullable String title) { - titleView.setText(title); - } -} diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/widgets/WaitingDialog.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/widgets/WaitingDialog.java deleted file mode 100644 index 19986c71..00000000 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/widgets/WaitingDialog.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.sendbird.uikit.customsample.widgets; - -import android.app.Dialog; -import android.content.Context; -import android.content.DialogInterface; -import android.os.Handler; -import android.os.Looper; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.sendbird.uikit.customsample.R; - - -public class WaitingDialog { - private final static String TAG = "logger"; - private final static Handler mainHandler = new Handler(Looper.getMainLooper()); - private static final Object waitingDialogLock = new Object(); - private static Dialog waitingDialog; - - - private static Dialog getWaitingDialog(@NonNull Context context) { - synchronized (waitingDialogLock) { - if (waitingDialog != null) { - return waitingDialog; - } - - waitingDialog = new Dialog(context, R.style.Widget_Sendbird_SendbirdProgressDialog); - return waitingDialog; - } - } - - public static void show(@NonNull Context context) { - show(context, false); - } - - public static void show(@NonNull Context context, int layoutResId) { - show(context, false, layoutResId, null); - } - - public static void show(@NonNull Context context, final boolean cancelable) { - show(context, cancelable, 0, null); - } - - public static void show(@NonNull Context context, final boolean cancelable, final int layoutResId, @Nullable DialogInterface.OnCancelListener listener) { - dismiss(); - - mainHandler.post(() -> { - Log.d(TAG, ">> WaitingDialog::show()"); - waitingDialog = getWaitingDialog(context); - // here we set layout of progress dialog - if (layoutResId <= 0) { - waitingDialog.setContentView(R.layout.sb_view_waiting_dialog); - } else { - waitingDialog.setContentView(layoutResId); - } - waitingDialog.setCancelable(cancelable); - if (listener != null) { - waitingDialog.setOnCancelListener(listener); - } - waitingDialog.show(); - }); - } - - public static void dismiss() { - mainHandler.post(() -> { - try { - Log.d(TAG, ">> WaitingDialog::cancel()"); - if (waitingDialog != null) { - synchronized (waitingDialogLock) { - waitingDialog.cancel(); - waitingDialog = null; - } - } - } catch (Exception e) { - Log.d(TAG, "", e); - } - }); - } -} diff --git a/uikit-custom-sample/src/main/res/color/selector_tab_tint.xml b/uikit-custom-sample/src/main/res/color/selector_tab_tint.xml deleted file mode 100644 index 373421b6..00000000 --- a/uikit-custom-sample/src/main/res/color/selector_tab_tint.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/color/selector_tab_tint_dark.xml b/uikit-custom-sample/src/main/res/color/selector_tab_tint_dark.xml deleted file mode 100644 index 04fd7b36..00000000 --- a/uikit-custom-sample/src/main/res/color/selector_tab_tint_dark.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/drawable-hdpi/home_card_bg.9.png b/uikit-custom-sample/src/main/res/drawable-hdpi/home_card_bg.9.png deleted file mode 100644 index 9821eb49..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-hdpi/home_card_bg.9.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-hdpi/ic_chat_hide.png b/uikit-custom-sample/src/main/res/drawable-hdpi/ic_chat_hide.png deleted file mode 100644 index b991f5cb..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-hdpi/ic_chat_hide.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-hdpi/ic_chat_show.png b/uikit-custom-sample/src/main/res/drawable-hdpi/ic_chat_show.png deleted file mode 100644 index a3971da7..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-hdpi/ic_chat_show.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-hdpi/icon_add.png b/uikit-custom-sample/src/main/res/drawable-hdpi/icon_add.png deleted file mode 100644 index 8c0dbebc..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-hdpi/icon_add.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-hdpi/icon_arrow_left.png b/uikit-custom-sample/src/main/res/drawable-hdpi/icon_arrow_left.png deleted file mode 100644 index bc725b99..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-hdpi/icon_arrow_left.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-hdpi/icon_channels.png b/uikit-custom-sample/src/main/res/drawable-hdpi/icon_channels.png deleted file mode 100644 index 1aa37f45..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-hdpi/icon_channels.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-hdpi/icon_chat_filled.png b/uikit-custom-sample/src/main/res/drawable-hdpi/icon_chat_filled.png deleted file mode 100644 index 8a03087b..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-hdpi/icon_chat_filled.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-hdpi/icon_create.png b/uikit-custom-sample/src/main/res/drawable-hdpi/icon_create.png deleted file mode 100644 index c6f64606..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-hdpi/icon_create.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-hdpi/icon_info.png b/uikit-custom-sample/src/main/res/drawable-hdpi/icon_info.png deleted file mode 100644 index c80555f7..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-hdpi/icon_info.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-hdpi/icon_leave.png b/uikit-custom-sample/src/main/res/drawable-hdpi/icon_leave.png deleted file mode 100644 index 8ea04146..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-hdpi/icon_leave.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-hdpi/icon_members.png b/uikit-custom-sample/src/main/res/drawable-hdpi/icon_members.png deleted file mode 100644 index f7759d06..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-hdpi/icon_members.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-hdpi/icon_notifications_filled.png b/uikit-custom-sample/src/main/res/drawable-hdpi/icon_notifications_filled.png deleted file mode 100644 index a99ee87d..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-hdpi/icon_notifications_filled.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-hdpi/icon_push_lollipop.png b/uikit-custom-sample/src/main/res/drawable-hdpi/icon_push_lollipop.png deleted file mode 100644 index 2186f719..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-hdpi/icon_push_lollipop.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-hdpi/icon_push_oreo.png b/uikit-custom-sample/src/main/res/drawable-hdpi/icon_push_oreo.png deleted file mode 100644 index d002c31a..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-hdpi/icon_push_oreo.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-hdpi/icon_send.png b/uikit-custom-sample/src/main/res/drawable-hdpi/icon_send.png deleted file mode 100644 index 649209b9..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-hdpi/icon_send.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-hdpi/icon_settings_filled.png b/uikit-custom-sample/src/main/res/drawable-hdpi/icon_settings_filled.png deleted file mode 100644 index 1f961dfd..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-hdpi/icon_settings_filled.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-hdpi/icon_streaming.png b/uikit-custom-sample/src/main/res/drawable-hdpi/icon_streaming.png deleted file mode 100644 index 961a155f..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-hdpi/icon_streaming.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-hdpi/img_groupchannel.png b/uikit-custom-sample/src/main/res/drawable-hdpi/img_groupchannel.png deleted file mode 100644 index bfc290ef..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-hdpi/img_groupchannel.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-hdpi/img_openchannel.png b/uikit-custom-sample/src/main/res/drawable-hdpi/img_openchannel.png deleted file mode 100644 index eba504a9..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-hdpi/img_openchannel.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-hdpi/logo_sendbird.png b/uikit-custom-sample/src/main/res/drawable-hdpi/logo_sendbird.png deleted file mode 100644 index 5c1bfad3..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-hdpi/logo_sendbird.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-hdpi/logo_sendbird_full.png b/uikit-custom-sample/src/main/res/drawable-hdpi/logo_sendbird_full.png deleted file mode 100644 index b72e745d..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-hdpi/logo_sendbird_full.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-mdpi/home_card_bg.9.png b/uikit-custom-sample/src/main/res/drawable-mdpi/home_card_bg.9.png deleted file mode 100644 index 47a1cbdc..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-mdpi/home_card_bg.9.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-mdpi/ic_chat_hide.png b/uikit-custom-sample/src/main/res/drawable-mdpi/ic_chat_hide.png deleted file mode 100644 index 4ba769ca..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-mdpi/ic_chat_hide.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-mdpi/ic_chat_show.png b/uikit-custom-sample/src/main/res/drawable-mdpi/ic_chat_show.png deleted file mode 100644 index e1a7c86a..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-mdpi/ic_chat_show.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-mdpi/icon_add.png b/uikit-custom-sample/src/main/res/drawable-mdpi/icon_add.png deleted file mode 100644 index c90a3722..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-mdpi/icon_add.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-mdpi/icon_arrow_left.png b/uikit-custom-sample/src/main/res/drawable-mdpi/icon_arrow_left.png deleted file mode 100644 index 1a886705..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-mdpi/icon_arrow_left.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-mdpi/icon_channels.png b/uikit-custom-sample/src/main/res/drawable-mdpi/icon_channels.png deleted file mode 100644 index 58aeac9a..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-mdpi/icon_channels.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-mdpi/icon_chat_filled.png b/uikit-custom-sample/src/main/res/drawable-mdpi/icon_chat_filled.png deleted file mode 100644 index d234a1e4..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-mdpi/icon_chat_filled.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-mdpi/icon_create.png b/uikit-custom-sample/src/main/res/drawable-mdpi/icon_create.png deleted file mode 100644 index 28a8eff0..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-mdpi/icon_create.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-mdpi/icon_info.png b/uikit-custom-sample/src/main/res/drawable-mdpi/icon_info.png deleted file mode 100644 index ff1a6dd6..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-mdpi/icon_info.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-mdpi/icon_leave.png b/uikit-custom-sample/src/main/res/drawable-mdpi/icon_leave.png deleted file mode 100644 index a89a9955..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-mdpi/icon_leave.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-mdpi/icon_members.png b/uikit-custom-sample/src/main/res/drawable-mdpi/icon_members.png deleted file mode 100644 index c9403274..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-mdpi/icon_members.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-mdpi/icon_notifications_filled.png b/uikit-custom-sample/src/main/res/drawable-mdpi/icon_notifications_filled.png deleted file mode 100644 index 26176b95..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-mdpi/icon_notifications_filled.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-mdpi/icon_push_lollipop.png b/uikit-custom-sample/src/main/res/drawable-mdpi/icon_push_lollipop.png deleted file mode 100644 index 3a0a0649..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-mdpi/icon_push_lollipop.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-mdpi/icon_push_oreo.png b/uikit-custom-sample/src/main/res/drawable-mdpi/icon_push_oreo.png deleted file mode 100644 index c2512e98..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-mdpi/icon_push_oreo.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-mdpi/icon_send.png b/uikit-custom-sample/src/main/res/drawable-mdpi/icon_send.png deleted file mode 100644 index 5ad58da7..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-mdpi/icon_send.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-mdpi/icon_settings_filled.png b/uikit-custom-sample/src/main/res/drawable-mdpi/icon_settings_filled.png deleted file mode 100644 index aa2fb5ac..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-mdpi/icon_settings_filled.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-mdpi/icon_streaming.png b/uikit-custom-sample/src/main/res/drawable-mdpi/icon_streaming.png deleted file mode 100644 index a2706b0a..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-mdpi/icon_streaming.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-mdpi/img_groupchannel.png b/uikit-custom-sample/src/main/res/drawable-mdpi/img_groupchannel.png deleted file mode 100644 index a6d49f2f..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-mdpi/img_groupchannel.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-mdpi/img_openchannel.png b/uikit-custom-sample/src/main/res/drawable-mdpi/img_openchannel.png deleted file mode 100644 index 3344e7c0..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-mdpi/img_openchannel.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-mdpi/logo_sendbird.png b/uikit-custom-sample/src/main/res/drawable-mdpi/logo_sendbird.png deleted file mode 100644 index 69d8ddf1..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-mdpi/logo_sendbird.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-mdpi/logo_sendbird_full.png b/uikit-custom-sample/src/main/res/drawable-mdpi/logo_sendbird_full.png deleted file mode 100644 index c9296976..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-mdpi/logo_sendbird_full.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xhdpi/home_card_bg.9.png b/uikit-custom-sample/src/main/res/drawable-xhdpi/home_card_bg.9.png deleted file mode 100644 index 41fce76d..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xhdpi/home_card_bg.9.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xhdpi/ic_chat_hide.png b/uikit-custom-sample/src/main/res/drawable-xhdpi/ic_chat_hide.png deleted file mode 100644 index 7118bee1..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xhdpi/ic_chat_hide.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xhdpi/ic_chat_show.png b/uikit-custom-sample/src/main/res/drawable-xhdpi/ic_chat_show.png deleted file mode 100644 index e410dc62..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xhdpi/ic_chat_show.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_add.png b/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_add.png deleted file mode 100644 index 4d49264d..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_add.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_arrow_left.png b/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_arrow_left.png deleted file mode 100644 index 03374a91..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_arrow_left.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_channels.png b/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_channels.png deleted file mode 100644 index 2193fd88..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_channels.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_chat_filled.png b/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_chat_filled.png deleted file mode 100644 index a2ab3c96..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_chat_filled.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_create.png b/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_create.png deleted file mode 100644 index 0d9e05a9..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_create.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_info.png b/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_info.png deleted file mode 100644 index 7c61abed..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_info.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_leave.png b/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_leave.png deleted file mode 100644 index 13202b4a..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_leave.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_members.png b/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_members.png deleted file mode 100644 index ec61347a..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_members.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_notifications_filled.png b/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_notifications_filled.png deleted file mode 100644 index e26c7214..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_notifications_filled.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_push_lollipop.png b/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_push_lollipop.png deleted file mode 100644 index 2a6ff6aa..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_push_lollipop.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_push_oreo.png b/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_push_oreo.png deleted file mode 100644 index d24b8b92..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_push_oreo.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_send.png b/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_send.png deleted file mode 100644 index 8bdd61b8..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_send.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_settings_filled.png b/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_settings_filled.png deleted file mode 100644 index 0cabd6b4..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_settings_filled.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_streaming.png b/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_streaming.png deleted file mode 100644 index f68f03a9..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xhdpi/icon_streaming.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xhdpi/img_groupchannel.png b/uikit-custom-sample/src/main/res/drawable-xhdpi/img_groupchannel.png deleted file mode 100644 index 2c117c58..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xhdpi/img_groupchannel.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xhdpi/img_openchannel.png b/uikit-custom-sample/src/main/res/drawable-xhdpi/img_openchannel.png deleted file mode 100644 index 0e3016ab..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xhdpi/img_openchannel.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xhdpi/logo_sendbird.png b/uikit-custom-sample/src/main/res/drawable-xhdpi/logo_sendbird.png deleted file mode 100644 index 781c85fc..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xhdpi/logo_sendbird.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xhdpi/logo_sendbird_full.png b/uikit-custom-sample/src/main/res/drawable-xhdpi/logo_sendbird_full.png deleted file mode 100644 index e93da43f..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xhdpi/logo_sendbird_full.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxhdpi/home_card_bg.9.png b/uikit-custom-sample/src/main/res/drawable-xxhdpi/home_card_bg.9.png deleted file mode 100644 index 429edce2..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxhdpi/home_card_bg.9.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxhdpi/ic_chat_hide.png b/uikit-custom-sample/src/main/res/drawable-xxhdpi/ic_chat_hide.png deleted file mode 100644 index 36138fdb..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxhdpi/ic_chat_hide.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxhdpi/ic_chat_show.png b/uikit-custom-sample/src/main/res/drawable-xxhdpi/ic_chat_show.png deleted file mode 100644 index 200ece0d..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxhdpi/ic_chat_show.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_add.png b/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_add.png deleted file mode 100644 index df4d8fea..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_add.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_arrow_left.png b/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_arrow_left.png deleted file mode 100644 index 65fd66a2..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_arrow_left.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_channels.png b/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_channels.png deleted file mode 100644 index 7936e3e7..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_channels.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_chat_filled.png b/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_chat_filled.png deleted file mode 100644 index 6c8c16ea..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_chat_filled.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_create.png b/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_create.png deleted file mode 100644 index 1168ecdd..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_create.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_info.png b/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_info.png deleted file mode 100644 index f2db23d5..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_info.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_leave.png b/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_leave.png deleted file mode 100644 index 5ce541b2..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_leave.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_members.png b/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_members.png deleted file mode 100644 index 5d652ffb..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_members.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_notifications_filled.png b/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_notifications_filled.png deleted file mode 100644 index 24916b2a..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_notifications_filled.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_push_lollipop.png b/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_push_lollipop.png deleted file mode 100644 index 24b6dfb9..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_push_lollipop.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_push_oreo.png b/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_push_oreo.png deleted file mode 100644 index 41185ede..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_push_oreo.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_send.png b/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_send.png deleted file mode 100644 index 5cb35692..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_send.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_settings_filled.png b/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_settings_filled.png deleted file mode 100644 index 059ffb69..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_settings_filled.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_streaming.png b/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_streaming.png deleted file mode 100644 index 97927a18..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxhdpi/icon_streaming.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxhdpi/img_groupchannel.png b/uikit-custom-sample/src/main/res/drawable-xxhdpi/img_groupchannel.png deleted file mode 100644 index 1eb6acd4..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxhdpi/img_groupchannel.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxhdpi/img_openchannel.png b/uikit-custom-sample/src/main/res/drawable-xxhdpi/img_openchannel.png deleted file mode 100644 index 8e9c775f..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxhdpi/img_openchannel.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxhdpi/logo_sendbird.png b/uikit-custom-sample/src/main/res/drawable-xxhdpi/logo_sendbird.png deleted file mode 100644 index 8dfa42d6..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxhdpi/logo_sendbird.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxhdpi/logo_sendbird_full.png b/uikit-custom-sample/src/main/res/drawable-xxhdpi/logo_sendbird_full.png deleted file mode 100644 index 7404501a..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxhdpi/logo_sendbird_full.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/home_card_bg.9.png b/uikit-custom-sample/src/main/res/drawable-xxxhdpi/home_card_bg.9.png deleted file mode 100644 index 076347e7..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/home_card_bg.9.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/ic_chat_hide.png b/uikit-custom-sample/src/main/res/drawable-xxxhdpi/ic_chat_hide.png deleted file mode 100644 index be42b6f2..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/ic_chat_hide.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/ic_chat_show.png b/uikit-custom-sample/src/main/res/drawable-xxxhdpi/ic_chat_show.png deleted file mode 100644 index 51e94900..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/ic_chat_show.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_add.png b/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_add.png deleted file mode 100644 index e3ae542e..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_add.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_arrow_left.png b/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_arrow_left.png deleted file mode 100644 index 3eb44265..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_arrow_left.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_channels.png b/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_channels.png deleted file mode 100644 index 7731401d..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_channels.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_chat_filled.png b/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_chat_filled.png deleted file mode 100644 index 083d9c0e..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_chat_filled.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_create.png b/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_create.png deleted file mode 100644 index 3b2464d2..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_create.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_info.png b/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_info.png deleted file mode 100644 index 9d3eb100..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_info.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_leave.png b/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_leave.png deleted file mode 100644 index 07f7db33..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_leave.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_members.png b/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_members.png deleted file mode 100644 index a0567ac8..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_members.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_notifications_filled.png b/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_notifications_filled.png deleted file mode 100644 index 4745c021..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_notifications_filled.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_push_lollipop.png b/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_push_lollipop.png deleted file mode 100644 index a2e8e63b..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_push_lollipop.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_push_oreo.png b/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_push_oreo.png deleted file mode 100644 index 40e8b060..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_push_oreo.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_send.png b/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_send.png deleted file mode 100644 index 83156f39..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_send.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_settings_filled.png b/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_settings_filled.png deleted file mode 100644 index f385dab4..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_settings_filled.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_streaming.png b/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_streaming.png deleted file mode 100644 index e78b4947..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/icon_streaming.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/img_groupchannel.png b/uikit-custom-sample/src/main/res/drawable-xxxhdpi/img_groupchannel.png deleted file mode 100644 index c4f65814..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/img_groupchannel.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/img_openchannel.png b/uikit-custom-sample/src/main/res/drawable-xxxhdpi/img_openchannel.png deleted file mode 100644 index 010d4589..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/img_openchannel.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/logo_sendbird.png b/uikit-custom-sample/src/main/res/drawable-xxxhdpi/logo_sendbird.png deleted file mode 100644 index d83fc20c..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/logo_sendbird.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/logo_sendbird_full.png b/uikit-custom-sample/src/main/res/drawable-xxxhdpi/logo_sendbird_full.png deleted file mode 100644 index 9454a4ab..00000000 Binary files a/uikit-custom-sample/src/main/res/drawable-xxxhdpi/logo_sendbird_full.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/drawable/custom_unread_count_background.xml b/uikit-custom-sample/src/main/res/drawable/custom_unread_count_background.xml deleted file mode 100644 index 913a2b68..00000000 --- a/uikit-custom-sample/src/main/res/drawable/custom_unread_count_background.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/drawable/highlight_message_background.xml b/uikit-custom-sample/src/main/res/drawable/highlight_message_background.xml deleted file mode 100644 index 3c9eee95..00000000 --- a/uikit-custom-sample/src/main/res/drawable/highlight_message_background.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/drawable/sb_button_uncontained_background_custom.xml b/uikit-custom-sample/src/main/res/drawable/sb_button_uncontained_background_custom.xml deleted file mode 100644 index b57814c1..00000000 --- a/uikit-custom-sample/src/main/res/drawable/sb_button_uncontained_background_custom.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/drawable/selector_button_primary_300.xml b/uikit-custom-sample/src/main/res/drawable/selector_button_primary_300.xml deleted file mode 100644 index 2ff9611d..00000000 --- a/uikit-custom-sample/src/main/res/drawable/selector_button_primary_300.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/drawable/selector_edit_text_clear_button_light.xml b/uikit-custom-sample/src/main/res/drawable/selector_edit_text_clear_button_light.xml deleted file mode 100644 index ef74e70b..00000000 --- a/uikit-custom-sample/src/main/res/drawable/selector_edit_text_clear_button_light.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/drawable/selector_edittext_background_with_focusable.xml b/uikit-custom-sample/src/main/res/drawable/selector_edittext_background_with_focusable.xml deleted file mode 100644 index 9c4f2edc..00000000 --- a/uikit-custom-sample/src/main/res/drawable/selector_edittext_background_with_focusable.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/drawable/selector_home_channel_type_button.xml b/uikit-custom-sample/src/main/res/drawable/selector_home_channel_type_button.xml deleted file mode 100644 index ec7c2988..00000000 --- a/uikit-custom-sample/src/main/res/drawable/selector_home_channel_type_button.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/drawable/selector_home_signout_button.xml b/uikit-custom-sample/src/main/res/drawable/selector_home_signout_button.xml deleted file mode 100644 index 2800f882..00000000 --- a/uikit-custom-sample/src/main/res/drawable/selector_home_signout_button.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/drawable/selector_list_background_light.xml b/uikit-custom-sample/src/main/res/drawable/selector_list_background_light.xml deleted file mode 100644 index b104b5b9..00000000 --- a/uikit-custom-sample/src/main/res/drawable/selector_list_background_light.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/drawable/shape_badge_background.xml b/uikit-custom-sample/src/main/res/drawable/shape_badge_background.xml deleted file mode 100644 index 9749dd4f..00000000 --- a/uikit-custom-sample/src/main/res/drawable/shape_badge_background.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/drawable/shape_badge_background_dark.xml b/uikit-custom-sample/src/main/res/drawable/shape_badge_background_dark.xml deleted file mode 100644 index 9749dd4f..00000000 --- a/uikit-custom-sample/src/main/res/drawable/shape_badge_background_dark.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/drawable/shape_cursor_drawable.xml b/uikit-custom-sample/src/main/res/drawable/shape_cursor_drawable.xml deleted file mode 100644 index 034c0113..00000000 --- a/uikit-custom-sample/src/main/res/drawable/shape_cursor_drawable.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/drawable/shape_cursor_drawable_bg_300.xml b/uikit-custom-sample/src/main/res/drawable/shape_cursor_drawable_bg_300.xml deleted file mode 100644 index 31f50576..00000000 --- a/uikit-custom-sample/src/main/res/drawable/shape_cursor_drawable_bg_300.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/drawable/shape_edit_text_background_light.xml b/uikit-custom-sample/src/main/res/drawable/shape_edit_text_background_light.xml deleted file mode 100644 index bf1b45a7..00000000 --- a/uikit-custom-sample/src/main/res/drawable/shape_edit_text_background_light.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/drawable/shape_image_view_background_light.xml b/uikit-custom-sample/src/main/res/drawable/shape_image_view_background_light.xml deleted file mode 100644 index 79c2bb85..00000000 --- a/uikit-custom-sample/src/main/res/drawable/shape_image_view_background_light.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/drawable/shape_live_badge_light.xml b/uikit-custom-sample/src/main/res/drawable/shape_live_badge_light.xml deleted file mode 100644 index dfcebeda..00000000 --- a/uikit-custom-sample/src/main/res/drawable/shape_live_badge_light.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/drawable/shape_live_indicator.xml b/uikit-custom-sample/src/main/res/drawable/shape_live_indicator.xml deleted file mode 100644 index 006a6cc0..00000000 --- a/uikit-custom-sample/src/main/res/drawable/shape_live_indicator.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/drawable/shape_oval.xml b/uikit-custom-sample/src/main/res/drawable/shape_oval.xml deleted file mode 100644 index 0d923d2b..00000000 --- a/uikit-custom-sample/src/main/res/drawable/shape_oval.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/drawable/shape_rect_transparent.xml b/uikit-custom-sample/src/main/res/drawable/shape_rect_transparent.xml deleted file mode 100644 index 037168f7..00000000 --- a/uikit-custom-sample/src/main/res/drawable/shape_rect_transparent.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/drawable/shape_round_rect_background_100_with_underline.xml b/uikit-custom-sample/src/main/res/drawable/shape_round_rect_background_100_with_underline.xml deleted file mode 100644 index 73f8cd7e..00000000 --- a/uikit-custom-sample/src/main/res/drawable/shape_round_rect_background_100_with_underline.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/uikit-custom-sample/src/main/res/drawable/shape_round_rect_primary_500.xml b/uikit-custom-sample/src/main/res/drawable/shape_round_rect_primary_500.xml deleted file mode 100644 index 89d2600d..00000000 --- a/uikit-custom-sample/src/main/res/drawable/shape_round_rect_primary_500.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/font/roboto_slab_bold.ttf b/uikit-custom-sample/src/main/res/font/roboto_slab_bold.ttf deleted file mode 100644 index 463745ac..00000000 Binary files a/uikit-custom-sample/src/main/res/font/roboto_slab_bold.ttf and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/font/roboto_slab_medium.ttf b/uikit-custom-sample/src/main/res/font/roboto_slab_medium.ttf deleted file mode 100644 index c6982215..00000000 Binary files a/uikit-custom-sample/src/main/res/font/roboto_slab_medium.ttf and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/font/roboto_slab_regular.ttf b/uikit-custom-sample/src/main/res/font/roboto_slab_regular.ttf deleted file mode 100644 index cc04d6ab..00000000 Binary files a/uikit-custom-sample/src/main/res/font/roboto_slab_regular.ttf and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/layout-land/activity_live_stream.xml b/uikit-custom-sample/src/main/res/layout-land/activity_live_stream.xml deleted file mode 100644 index 0c049119..00000000 --- a/uikit-custom-sample/src/main/res/layout-land/activity_live_stream.xml +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/layout/activity_home.xml b/uikit-custom-sample/src/main/res/layout/activity_home.xml deleted file mode 100644 index eabb8df8..00000000 --- a/uikit-custom-sample/src/main/res/layout/activity_home.xml +++ /dev/null @@ -1,200 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/layout/activity_live_stream.xml b/uikit-custom-sample/src/main/res/layout/activity_live_stream.xml deleted file mode 100644 index 5ec43542..00000000 --- a/uikit-custom-sample/src/main/res/layout/activity_live_stream.xml +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/layout/activity_login.xml b/uikit-custom-sample/src/main/res/layout/activity_login.xml deleted file mode 100644 index 3e772f6c..00000000 --- a/uikit-custom-sample/src/main/res/layout/activity_login.xml +++ /dev/null @@ -1,132 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/uikit-custom-sample/src/main/res/layout/activity_main.xml b/uikit-custom-sample/src/main/res/layout/activity_main.xml deleted file mode 100644 index 474509b6..00000000 --- a/uikit-custom-sample/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/layout/activity_open_channel_main.xml b/uikit-custom-sample/src/main/res/layout/activity_open_channel_main.xml deleted file mode 100644 index d12ce2c2..00000000 --- a/uikit-custom-sample/src/main/res/layout/activity_open_channel_main.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - - diff --git a/uikit-custom-sample/src/main/res/layout/activity_splash.xml b/uikit-custom-sample/src/main/res/layout/activity_splash.xml deleted file mode 100644 index 80f4c1cd..00000000 --- a/uikit-custom-sample/src/main/res/layout/activity_splash.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/layout/fragment_open_channel_list.xml b/uikit-custom-sample/src/main/res/layout/fragment_open_channel_list.xml deleted file mode 100644 index 8e1de4b3..00000000 --- a/uikit-custom-sample/src/main/res/layout/fragment_open_channel_list.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/layout/fragment_settings.xml b/uikit-custom-sample/src/main/res/layout/fragment_settings.xml deleted file mode 100644 index db3d9a39..00000000 --- a/uikit-custom-sample/src/main/res/layout/fragment_settings.xml +++ /dev/null @@ -1,202 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/layout/view_community_list_item.xml b/uikit-custom-sample/src/main/res/layout/view_community_list_item.xml deleted file mode 100644 index a7a08423..00000000 --- a/uikit-custom-sample/src/main/res/layout/view_community_list_item.xml +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/layout/view_custom_channel_holder.xml b/uikit-custom-sample/src/main/res/layout/view_custom_channel_holder.xml deleted file mode 100644 index 03f4b20b..00000000 --- a/uikit-custom-sample/src/main/res/layout/view_custom_channel_holder.xml +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/layout/view_custom_channel_input.xml b/uikit-custom-sample/src/main/res/layout/view_custom_channel_input.xml deleted file mode 100644 index de11fbc2..00000000 --- a/uikit-custom-sample/src/main/res/layout/view_custom_channel_input.xml +++ /dev/null @@ -1,132 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/layout/view_custom_channel_menu_search_button.xml b/uikit-custom-sample/src/main/res/layout/view_custom_channel_menu_search_button.xml deleted file mode 100644 index 334b3802..00000000 --- a/uikit-custom-sample/src/main/res/layout/view_custom_channel_menu_search_button.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - diff --git a/uikit-custom-sample/src/main/res/layout/view_custom_channel_menu_settings_button.xml b/uikit-custom-sample/src/main/res/layout/view_custom_channel_menu_settings_button.xml deleted file mode 100644 index 27fa7053..00000000 --- a/uikit-custom-sample/src/main/res/layout/view_custom_channel_menu_settings_button.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - diff --git a/uikit-custom-sample/src/main/res/layout/view_custom_channel_settings_menu.xml b/uikit-custom-sample/src/main/res/layout/view_custom_channel_settings_menu.xml deleted file mode 100644 index b1c3c08d..00000000 --- a/uikit-custom-sample/src/main/res/layout/view_custom_channel_settings_menu.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/layout/view_custom_channel_settings_menu_edit_button.xml b/uikit-custom-sample/src/main/res/layout/view_custom_channel_settings_menu_edit_button.xml deleted file mode 100644 index cd05fa77..00000000 --- a/uikit-custom-sample/src/main/res/layout/view_custom_channel_settings_menu_edit_button.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - diff --git a/uikit-custom-sample/src/main/res/layout/view_custom_channels_menu_create_button.xml b/uikit-custom-sample/src/main/res/layout/view_custom_channels_menu_create_button.xml deleted file mode 100644 index fe865778..00000000 --- a/uikit-custom-sample/src/main/res/layout/view_custom_channels_menu_create_button.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - diff --git a/uikit-custom-sample/src/main/res/layout/view_custom_channels_menu_settings_button.xml b/uikit-custom-sample/src/main/res/layout/view_custom_channels_menu_settings_button.xml deleted file mode 100644 index bae667a7..00000000 --- a/uikit-custom-sample/src/main/res/layout/view_custom_channels_menu_settings_button.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - diff --git a/uikit-custom-sample/src/main/res/layout/view_custom_menu_create_button.xml b/uikit-custom-sample/src/main/res/layout/view_custom_menu_create_button.xml deleted file mode 100644 index 52f276f1..00000000 --- a/uikit-custom-sample/src/main/res/layout/view_custom_menu_create_button.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - diff --git a/uikit-custom-sample/src/main/res/layout/view_custom_menu_edit_button.xml b/uikit-custom-sample/src/main/res/layout/view_custom_menu_edit_button.xml deleted file mode 100644 index 7d368c4a..00000000 --- a/uikit-custom-sample/src/main/res/layout/view_custom_menu_edit_button.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - diff --git a/uikit-custom-sample/src/main/res/layout/view_custom_menu_select_button.xml b/uikit-custom-sample/src/main/res/layout/view_custom_menu_select_button.xml deleted file mode 100644 index 71ef2705..00000000 --- a/uikit-custom-sample/src/main/res/layout/view_custom_menu_select_button.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - diff --git a/uikit-custom-sample/src/main/res/layout/view_custom_message_search_header.xml b/uikit-custom-sample/src/main/res/layout/view_custom_message_search_header.xml deleted file mode 100644 index a24b5b1f..00000000 --- a/uikit-custom-sample/src/main/res/layout/view_custom_message_search_header.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/layout/view_custom_moderation_list.xml b/uikit-custom-sample/src/main/res/layout/view_custom_moderation_list.xml deleted file mode 100644 index 4f03c99c..00000000 --- a/uikit-custom-sample/src/main/res/layout/view_custom_moderation_list.xml +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/layout/view_custom_searched_message_holder.xml b/uikit-custom-sample/src/main/res/layout/view_custom_searched_message_holder.xml deleted file mode 100644 index 820a1ce9..00000000 --- a/uikit-custom-sample/src/main/res/layout/view_custom_searched_message_holder.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/layout/view_custom_suggested_mention_holder.xml b/uikit-custom-sample/src/main/res/layout/view_custom_suggested_mention_holder.xml deleted file mode 100644 index a97edbc0..00000000 --- a/uikit-custom-sample/src/main/res/layout/view_custom_suggested_mention_holder.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/layout/view_custom_tab.xml b/uikit-custom-sample/src/main/res/layout/view_custom_tab.xml deleted file mode 100644 index 1e3f570e..00000000 --- a/uikit-custom-sample/src/main/res/layout/view_custom_tab.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/layout/view_custom_user_holder.xml b/uikit-custom-sample/src/main/res/layout/view_custom_user_holder.xml deleted file mode 100644 index d0382568..00000000 --- a/uikit-custom-sample/src/main/res/layout/view_custom_user_holder.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/layout/view_custom_user_typed_holder.xml b/uikit-custom-sample/src/main/res/layout/view_custom_user_typed_holder.xml deleted file mode 100644 index dd209b47..00000000 --- a/uikit-custom-sample/src/main/res/layout/view_custom_user_typed_holder.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/layout/view_custom_user_typed_menu_right_button.xml b/uikit-custom-sample/src/main/res/layout/view_custom_user_typed_menu_right_button.xml deleted file mode 100644 index 1af13f40..00000000 --- a/uikit-custom-sample/src/main/res/layout/view_custom_user_typed_menu_right_button.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - diff --git a/uikit-custom-sample/src/main/res/layout/view_emoji_message_me_holder.xml b/uikit-custom-sample/src/main/res/layout/view_emoji_message_me_holder.xml deleted file mode 100644 index 34fb2181..00000000 --- a/uikit-custom-sample/src/main/res/layout/view_emoji_message_me_holder.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - diff --git a/uikit-custom-sample/src/main/res/layout/view_emoji_message_other_holder.xml b/uikit-custom-sample/src/main/res/layout/view_emoji_message_other_holder.xml deleted file mode 100644 index 7b24e4cb..00000000 --- a/uikit-custom-sample/src/main/res/layout/view_emoji_message_other_holder.xml +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/layout/view_highlight_message_me_holder.xml b/uikit-custom-sample/src/main/res/layout/view_highlight_message_me_holder.xml deleted file mode 100644 index 63c6a32f..00000000 --- a/uikit-custom-sample/src/main/res/layout/view_highlight_message_me_holder.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - diff --git a/uikit-custom-sample/src/main/res/layout/view_highlight_message_other_holder.xml b/uikit-custom-sample/src/main/res/layout/view_highlight_message_other_holder.xml deleted file mode 100644 index b1b736ea..00000000 --- a/uikit-custom-sample/src/main/res/layout/view_highlight_message_other_holder.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/layout/view_live_stream_list_item.xml b/uikit-custom-sample/src/main/res/layout/view_live_stream_list_item.xml deleted file mode 100644 index 9a8e5825..00000000 --- a/uikit-custom-sample/src/main/res/layout/view_live_stream_list_item.xml +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/layout/view_open_channel_highlight_message_holder.xml b/uikit-custom-sample/src/main/res/layout/view_open_channel_highlight_message_holder.xml deleted file mode 100644 index 58d02988..00000000 --- a/uikit-custom-sample/src/main/res/layout/view_open_channel_highlight_message_holder.xml +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/uikit-custom-sample/src/main/res/menu/channel_menu.xml b/uikit-custom-sample/src/main/res/menu/channel_menu.xml deleted file mode 100644 index 77723e84..00000000 --- a/uikit-custom-sample/src/main/res/menu/channel_menu.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/menu/channel_settings_menu.xml b/uikit-custom-sample/src/main/res/menu/channel_settings_menu.xml deleted file mode 100644 index 8c027e35..00000000 --- a/uikit-custom-sample/src/main/res/menu/channel_settings_menu.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/menu/channels_menu.xml b/uikit-custom-sample/src/main/res/menu/channels_menu.xml deleted file mode 100644 index db715d80..00000000 --- a/uikit-custom-sample/src/main/res/menu/channels_menu.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/menu/community_list_menu.xml b/uikit-custom-sample/src/main/res/menu/community_list_menu.xml deleted file mode 100644 index aea349b4..00000000 --- a/uikit-custom-sample/src/main/res/menu/community_list_menu.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/menu/select_user_menu.xml b/uikit-custom-sample/src/main/res/menu/select_user_menu.xml deleted file mode 100644 index e4f6c088..00000000 --- a/uikit-custom-sample/src/main/res/menu/select_user_menu.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/menu/settings_menu.xml b/uikit-custom-sample/src/main/res/menu/settings_menu.xml deleted file mode 100644 index a493da2c..00000000 --- a/uikit-custom-sample/src/main/res/menu/settings_menu.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/menu/user_typed_menu.xml b/uikit-custom-sample/src/main/res/menu/user_typed_menu.xml deleted file mode 100644 index 24391fb7..00000000 --- a/uikit-custom-sample/src/main/res/menu/user_typed_menu.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/mipmap-hdpi/ic_launcher.png b/uikit-custom-sample/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 9e722e4b..00000000 Binary files a/uikit-custom-sample/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/mipmap-mdpi/ic_launcher.png b/uikit-custom-sample/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index e822da95..00000000 Binary files a/uikit-custom-sample/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/mipmap-xhdpi/ic_launcher.png b/uikit-custom-sample/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 5b720a47..00000000 Binary files a/uikit-custom-sample/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/uikit-custom-sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index eebf8db4..00000000 Binary files a/uikit-custom-sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/uikit-custom-sample/src/main/res/values-ko-rKR/strings.xml b/uikit-custom-sample/src/main/res/values-ko-rKR/strings.xml deleted file mode 100644 index 0ed4d768..00000000 --- a/uikit-custom-sample/src/main/res/values-ko-rKR/strings.xml +++ /dev/null @@ -1,189 +0,0 @@ - - - - - 채널 - 채널 정보 - 멤버 - 초대하기 - 새 채널 - - - (빈 채널) - (알 수 없는 사용자) - 99+ - %1$s가 보낸 파일 - 알 수 없음 - 푸시 알림 켜기 - 푸시 알림 끄기 - 나가기 - 아직 채널이 없습니다. - - -  (편집됨) - 새 메시지 %d개 - 새 메시지 %d개 - 복사 - 저장 - 편집 - 삭제 - 메시지를 입력하세요 - 카메라 - 포토 갤러리 - 파일 - %s 입력 중… - %s와 %s가 입력 중… - 여러 명이 입력 중… - %s 전 마지막으로 확인 - (알 수 없는 타입의 메시지)\n이 메시지를 읽을 수 없습니다. - - - 관리 - 알림 - 멤버 - 채널 나가기 - 채널 이름 변경 - 채널 이름을 입력하세요 - 채널 이미지 변경 - 사진 찍기 - 포토 갤러리에서 가져오기 - - -  (나) - 아직 사용자가 없습니다. - - - 어제 - - 아니오 - 파일 삭제하기 - 메시지 삭제하기 - 푸시 알림 켜짐 - 푸시 알림 꺼짐 - 복사 완료 - 업로드 중… - 다운로드 중… - 파일 저장 완료 - 편집 - 초대 - %d명 - 취소 - 만들기 - %d명 - 저장 - 삭제 - 재시도 - - - - - - Couldn\'t connect to server. - Check your connection and try again. - - - Couldn\'t retrieve channel list. - Couldn\'t retrieve channel. - Couldn\'t create channel. - Couldn\'t update channel. - Couldn\'t leave channel. - Couldn\'t turn on notifications. - Couldn\'t turn off notifications. - Couldn\'t invite users. - Couldn\'t retrieve user list. - - - Couldn\'t send message. Try again. - Couldn\'t edit message. - Couldn\'t delete message. - Couldn\'t copy message. - Couldn\'t send message. Try again. - - - Couldn\'t open camera. - Couldn\'t open photo library. - Couldn\'t open file. - Couldn\'t download file. - - Something went wrong. - - Allow permission - Settings - %s\nneeds to access your camera to take photos and videos. Allow in Settings. - %s\nneeds to access your device storage to share photos, media, and files. Allow in Settings. - - 메시지를 입력할 수 없습니다 - 음소거 되었습니다. - 채널 타입 - 그룹 - 슈퍼그룹 - 공지 - 일시 정지된 채널입니다 - 관리자 - 관리자 - 음소거된 멤버 - 접근 금지된 유저 - 채널 일시 정지하기 - 관리자가 없습니다. - 음소거된 멤버가 없습니다. - 접근 금지된 유저가 없습니다. - - 선택 - 추가 - 음소거 - 접근 금지 - %d명 - %d명 - %d명 - - 관리자로 지정 - 음소거 - 접근 금지 - 관리자 해제 - 음소거 해제 - 접근 금지 해제 - - 설정 - 편집 - 아이디 - 방해 금지 모드 - 홈으로 나가기 - 닉네임 변경 - 프로필 이미지 변경 - - 채널 - 설정 - 99+ - - 커뮤니티 만들기 - 라이브 - 커뮤니티 - UIKit sample을 위한 예시 채널입니다. - 만들기 - 채널 이름을 입력하세요 - 라이브 - %s명 - - - 채널을 나가시겠습니까? - 확인 - %d명 선택 - - SendbirdUIKitCustomSample - Sendbird UIKit Sample - - UI Kit v%1$s      SDK v%2$s - 사용자 ID - 사용자 닉네임 - SIGN IN - - SIGN OUT - Home - Group channel - Open channel - 1 on 1, Group chat with members - Live streams, Open community chat - %s\n푸시 알림을 받기위해 알림설정이 필요합니다. 권한을 받기위해 설정으로 이동하세요. - diff --git a/uikit-custom-sample/src/main/res/values/colors.xml b/uikit-custom-sample/src/main/res/values/colors.xml deleted file mode 100644 index 35c8d97b..00000000 --- a/uikit-custom-sample/src/main/res/values/colors.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - #0091FF - #006BBD - #2EBA9F - #F78900 - - #006BBD - #007CBD - #0091FF - #35A8FF - #85CAFF - #066858 - #027d69 - #259c72 - #69c085 - #a8e2ab - #9d091e - #bf0711 - #de360b - #f66161 - #fdaaaa - #000000 - #161616 - #2c2c2c - #393939 - #bdbdbd - #e0e0e0 - #eeeeee - #ffffff - #8c000000 - #51000000 - #e1000000 - #80000000 - #61000000 - #1e000000 - #e1ffffff - #80ffffff - #61ffffff - #1effffff - #adc9ff - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/values/sendbird_text_appearance.xml b/uikit-custom-sample/src/main/res/values/sendbird_text_appearance.xml deleted file mode 100755 index fbf15fa2..00000000 --- a/uikit-custom-sample/src/main/res/values/sendbird_text_appearance.xml +++ /dev/null @@ -1,1216 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/values/strings.xml b/uikit-custom-sample/src/main/res/values/strings.xml deleted file mode 100644 index 89f38e04..00000000 --- a/uikit-custom-sample/src/main/res/values/strings.xml +++ /dev/null @@ -1,42 +0,0 @@ - - SendbirdUIKitCustomSample - Sendbird UIKit Sample - - UI Kit v%1$s      SDK v%2$s - User ID - Nickname - SIGN IN - - My settings - EDIT - User ID - Do not disturb - Exit to home - Change nickname - Change profile image - - Channels - Settings - 99+ - - SIGN OUT - Home - Group channel - Open channel - 1 on 1, Group chat with members - Live streams, Open community chat - Create community - Live streams - Community - Preset channels developed by UIKit - Create - Enter channel name - LIVE - %s participants - - - Leave this channel? - Okay - %d seleted - %s\nneeds permission to receive push notification. Go to Settings to allow access - diff --git a/uikit-custom-sample/src/main/res/values/styles.xml b/uikit-custom-sample/src/main/res/values/styles.xml deleted file mode 100644 index 61c921d8..00000000 --- a/uikit-custom-sample/src/main/res/values/styles.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - \ No newline at end of file diff --git a/uikit-custom-sample/src/main/res/values/styles_custom.xml b/uikit-custom-sample/src/main/res/values/styles_custom.xml deleted file mode 100755 index d5fcdf74..00000000 --- a/uikit-custom-sample/src/main/res/values/styles_custom.xml +++ /dev/null @@ -1,214 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/uikit-custom-sample/src/test/java/com/sendbird/uikit/customsample/ExampleUnitTest.java b/uikit-custom-sample/src/test/java/com/sendbird/uikit/customsample/ExampleUnitTest.java deleted file mode 100644 index 741fa133..00000000 --- a/uikit-custom-sample/src/test/java/com/sendbird/uikit/customsample/ExampleUnitTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.sendbird.uikit.customsample; - -import static org.junit.Assert.*; - -import org.junit.Test; - -/** - * Example local unit test, which will execute on the development machine (host). - * - * @see Testing documentation - */ -public class ExampleUnitTest { - @Test - public void addition_isCorrect() { - assertEquals(4, 2 + 2); - } -} \ No newline at end of file diff --git a/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/BaseApplication.java b/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/BaseApplication.java index b6a63c6d..5fc87a23 100644 --- a/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/BaseApplication.java +++ b/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/BaseApplication.java @@ -25,7 +25,8 @@ */ public class BaseApplication extends MultiDexApplication { - private static final String APP_ID = "2D7B4CDB-932F-4082-9B09-A1153792DC8D"; + // this app is only used for Notification channel testing. + private static final String APP_ID = "60E22A13-CC2E-4E83-98BE-578E72FC92F3"; private static final MutableLiveData initState = new MutableLiveData<>(); /** diff --git a/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/LoginActivity.java b/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/LoginActivity.java index 8227479f..99abe92b 100644 --- a/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/LoginActivity.java +++ b/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/LoginActivity.java @@ -5,10 +5,15 @@ import android.text.Editable; import android.view.View; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import com.sendbird.android.SendbirdChat; +import com.sendbird.android.channel.GroupChannel; +import com.sendbird.android.handler.GroupChannelCallbackHandler; +import com.sendbird.android.params.GroupChannelCreateParams; +import com.sendbird.android.user.User; import com.sendbird.uikit.BuildConfig; import com.sendbird.uikit.SendbirdUIKit; import com.sendbird.uikit.log.Logger; @@ -19,6 +24,8 @@ import com.sendbird.uikit_messaging_android.utils.PushUtils; import com.sendbird.uikit_messaging_android.widgets.WaitingDialog; +import java.util.Collections; + /** * Displays a login screen. */ @@ -53,21 +60,79 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { WaitingDialog.show(this); SendbirdUIKit.connect((user, e) -> { - if (e != null) { + if (e != null || user == null) { Logger.e(e); WaitingDialog.dismiss(); PreferenceUtils.clearAll(); return; } - WaitingDialog.dismiss(); - PreferenceUtils.setUserId(userIdString); - PreferenceUtils.setNickname(userNickname.toString()); - - PushUtils.registerPushHandler(new MyFirebaseMessagingService()); - Intent intent = new Intent(LoginActivity.this, HomeActivity.class); - startActivity(intent); - finish(); + + createNotificationChannel(user, (groupChannel, e1) -> { + if (e1 != null) { + Logger.e(e1); + WaitingDialog.dismiss(); + PreferenceUtils.clearAll(); + return; + } + WaitingDialog.dismiss(); + PreferenceUtils.setUserId(userIdString); + PreferenceUtils.setNickname(userNickname.toString()); + + PushUtils.registerPushHandler(new MyFirebaseMessagingService()); + Intent intent = new Intent(LoginActivity.this, HomeActivity.class); + startActivity(intent); + finish(); + }); }); }); } + + private void createNotificationChannel(@NonNull User user, @NonNull GroupChannelCallbackHandler callback) { + final GroupChannelCreateParams params = new GroupChannelCreateParams(); + final String CUSTOM_TYPE = "SENDBIRD_NOTIFICATION_CHANNEL_NOTIFICATION"; + final String CHANNEL_NAME = "Notifications"; + + /* + The name of the channel. + It will appear on the Group channels menu of the Sendbird Dashboard. + It's meant to be a channel's display name on a user's channel list. + */ + params.setName(CHANNEL_NAME); + + /* + A custom channel type which is used for channel grouping. + The custom type of a notification channel must start with SENDBIRD_NOTIFICATION_CHANNEL_. + - The same custom type must be used for all users' group channel. + - It must not be assigned to other user-to-user group channels. + */ + params.setCustomType(CUSTOM_TYPE); + + /* + Use a combination of the custom type as a prefix and a user ID + (e.g. SENDBIRD_NOTIFICATION_CHANNEL_NOTIFICATION_user123) + so that a channel URL can be inferred from a user ID. + Only alphanumeric characters, hyphens, and underscores are allowed in a channel URL. + */ + params.setChannelUrl(CUSTOM_TYPE + "_" + user.getUserId()); + + /* + To allow a user to delete a notification from their own Notification Center, + add the notification receiver's user ID to operator_ids. + Channel operators can delete a message from the channel. + */ + params.setOperators(Collections.singletonList(SendbirdChat.getCurrentUser())); + + /* + Must be true to ensure that only one notification channel per a user exists and the channel can be properly targeted when Sendbird delivers a message to the user. + */ + params.setDistinct(true); + + /* + By setting strict to true, + you will get an error response when you try to create a channel with an unexisting user ID. + */ + params.setStrict(true); + + GroupChannel.createChannel(params, callback); + } } diff --git a/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/SettingsFragment.java b/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/SettingsFragment.java index 74ceca01..08766096 100644 --- a/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/SettingsFragment.java +++ b/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/SettingsFragment.java @@ -110,7 +110,7 @@ public void onCreate(@Nullable Bundle savedInstanceState) { @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { binding = FragmentSettingsBinding.inflate(inflater, container, false); - headerComponent.getParams().setTitle(getString(R.string.text_settings_header_title)); + headerComponent.getParams().setTitle(getString(R.string.text_tab_settings)); headerComponent.getParams().setUseLeftButton(false); headerComponent.getParams().setRightButtonText(getString(R.string.text_settings_header_edit_button)); final View header = headerComponent.onCreateView(requireContext(), inflater, binding.headerComponent, savedInstanceState); diff --git a/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/consts/StringSet.java b/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/consts/StringSet.java index ddc75336..47b2cd51 100644 --- a/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/consts/StringSet.java +++ b/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/consts/StringSet.java @@ -20,6 +20,7 @@ public class StringSet { public static final String sender = "sender"; public static final String name = "name"; public static final String profile_url = "profile_url"; + public static final String push_title = "push_title"; public static final String id = "id"; public static final String tags = "tags"; diff --git a/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/fcm/MyFirebaseMessagingService.java b/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/fcm/MyFirebaseMessagingService.java index 1eebf225..b836a222 100644 --- a/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/fcm/MyFirebaseMessagingService.java +++ b/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/fcm/MyFirebaseMessagingService.java @@ -88,18 +88,22 @@ public void onMessageReceived(@Nullable Context context, @Nullable RemoteMessage * @param sendBird JSONObject payload from FCM */ public static void sendNotification(@NonNull Context context, @NonNull JSONObject sendBird) throws JSONException { - String message = sendBird.getString(StringSet.message); - JSONObject channel = sendBird.getJSONObject(StringSet.channel); - String channelUrl = channel.getString(StringSet.channel_url); - long messageId = sendBird.getLong(StringSet.message_id); + final String message = sendBird.getString(StringSet.message); + final JSONObject channel = sendBird.getJSONObject(StringSet.channel); + final String channelUrl = channel.getString(StringSet.channel_url); + final long messageId = sendBird.getLong(StringSet.message_id); - String senderName = context.getString(R.string.app_name); + String pushTitle = context.getString(R.string.app_name); if (sendBird.has(StringSet.sender)) { JSONObject sender = sendBird.getJSONObject(StringSet.sender); - senderName = sender.getString(StringSet.name); + pushTitle = sender.getString(StringSet.name); } - NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + if (sendBird.has(StringSet.push_title) && !sendBird.isNull(StringSet.push_title)) { + pushTitle = sendBird.getString(StringSet.push_title); + } + + final NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); final String CHANNEL_ID = StringSet.CHANNEL_ID; if (Build.VERSION.SDK_INT >= 26) { // Build.VERSION_CODES.O @@ -119,7 +123,7 @@ public static void sendNotification(@NonNull Context context, @NonNull JSONObjec .setSmallIcon(R.drawable.icon_push_lollipop) .setColor(ContextCompat.getColor(context, R.color.primary_300)) // small icon background color .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.icon_push_oreo)) - .setContentTitle(senderName) + .setContentTitle(pushTitle) .setAutoCancel(true) .setSound(defaultSoundUri) .setPriority(Notification.PRIORITY_MAX) diff --git a/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/groupchannel/GroupChannelMainActivity.java b/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/groupchannel/GroupChannelMainActivity.java index e4f15736..4dd59d47 100644 --- a/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/groupchannel/GroupChannelMainActivity.java +++ b/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/groupchannel/GroupChannelMainActivity.java @@ -15,18 +15,25 @@ import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentPagerAdapter; +import com.google.android.material.tabs.TabLayout; import com.sendbird.android.SendbirdChat; +import com.sendbird.android.channel.GroupChannel; import com.sendbird.android.handler.UserEventHandler; +import com.sendbird.android.params.GroupChannelListQueryParams; import com.sendbird.android.params.GroupChannelTotalUnreadMessageCountParams; import com.sendbird.android.user.User; import com.sendbird.uikit.SendbirdUIKit; import com.sendbird.uikit.activities.ChannelActivity; +import com.sendbird.uikit.fragments.ChannelListFragment; +import com.sendbird.uikit.fragments.NotificationChannelFragment; +import com.sendbird.uikit.log.Logger; import com.sendbird.uikit_messaging_android.R; import com.sendbird.uikit_messaging_android.SettingsFragment; import com.sendbird.uikit_messaging_android.databinding.ActivityGroupChannelMainBinding; import com.sendbird.uikit_messaging_android.utils.PreferenceUtils; import com.sendbird.uikit_messaging_android.widgets.CustomTabView; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; @@ -36,9 +43,23 @@ */ public class GroupChannelMainActivity extends AppCompatActivity { private static final String USER_EVENT_HANDLER_KEY = "USER_EVENT_HANDLER_KEY" + System.currentTimeMillis(); + private final String notificationChannelCustomType = "SENDBIRD_NOTIFICATION_CHANNEL_NOTIFICATION"; + private static final int TAB_CHANNEL_LIST = 0; + private static final int TAB_CHANNEL_NOTIFICATION = 1; + private static final int TAB_SETTINGS = 2; private ActivityGroupChannelMainBinding binding; - private CustomTabView unreadCountTab; + private CustomTabView channelListTab; + private CustomTabView notificationTab; + + @NonNull + private static String getNotificationChannelUrl() { + final User user = SendbirdChat.getCurrentUser(); + if (user != null) { + return "SENDBIRD_NOTIFICATION_CHANNEL_NOTIFICATION_" + user.getUserId(); + } + throw new RuntimeException("user must exist"); + } @Override protected void onCreate(@Nullable Bundle savedInstanceState) { @@ -51,68 +72,116 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { } private void initPage() { - binding.vpMain.setAdapter(new MainAdapter(getSupportFragmentManager(), FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)); + final MainAdapter adapter = new MainAdapter(getSupportFragmentManager(), FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); + binding.vpMain.setAdapter(adapter); boolean isDarkMode = PreferenceUtils.isUsingDarkTheme(); int backgroundRedId = isDarkMode ? R.color.background_600 : R.color.background_50; binding.tlMain.setBackgroundResource(backgroundRedId); binding.tlMain.setupWithViewPager(binding.vpMain); - unreadCountTab = new CustomTabView(this); - unreadCountTab.setBadgeVisibility(View.GONE); - unreadCountTab.setTitle(getString(R.string.text_tab_channels)); - unreadCountTab.setIcon(R.drawable.icon_chat_filled); + channelListTab = new CustomTabView(this); + channelListTab.setBadgeVisibility(View.GONE); + channelListTab.setTitle(getString(R.string.text_tab_channels)); + channelListTab.setIcon(R.drawable.icon_chat_filled); + + notificationTab = new CustomTabView(this); + notificationTab.setBadgeVisibility(View.GONE); + notificationTab.setTitle(getString(R.string.text_tab_notifications)); + notificationTab.setIcon(R.drawable.icon_notifications_filled); CustomTabView settingsTab = new CustomTabView(this); settingsTab.setBadgeVisibility(View.GONE); settingsTab.setTitle(getString(R.string.text_tab_settings)); settingsTab.setIcon(R.drawable.icon_settings_filled); - Objects.requireNonNull(binding.tlMain.getTabAt(0)).setCustomView(unreadCountTab); - Objects.requireNonNull(binding.tlMain.getTabAt(1)).setCustomView(settingsTab); + Objects.requireNonNull(binding.tlMain.getTabAt(TAB_CHANNEL_LIST)).setCustomView(channelListTab); + Objects.requireNonNull(binding.tlMain.getTabAt(TAB_CHANNEL_NOTIFICATION)).setCustomView(notificationTab); + Objects.requireNonNull(binding.tlMain.getTabAt(TAB_SETTINGS)).setCustomView(settingsTab); redirectChannelIfNeeded(getIntent()); - } - @Override - protected void onResume() { - super.onResume(); - SendbirdChat.getTotalUnreadMessageCount(new GroupChannelTotalUnreadMessageCountParams(), (totalCount, e) -> { - if (e != null) { - return; + binding.tlMain.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { + @Override + public void onTabSelected(TabLayout.Tab tab) { + int selectedPosition = binding.tlMain.getSelectedTabPosition(); + Logger.d("++ onTabSelected selected tab position =%d", selectedPosition); + if (selectedPosition == TAB_CHANNEL_NOTIFICATION) { + final Fragment fragment = adapter.getItem(TAB_CHANNEL_NOTIFICATION); + if (fragment instanceof NotificationChannelFragment) { + ((NotificationChannelFragment) fragment).updateLastReadTimeOnCurrentChannel(); + } + } } - if (totalCount > 0) { - unreadCountTab.setBadgeVisibility(View.VISIBLE); - unreadCountTab.setBadgeCount(totalCount > 99 ? - getString(R.string.text_tab_badge_max_count) : - String.valueOf(totalCount)); - } else { - unreadCountTab.setBadgeVisibility(View.GONE); + @Override + public void onTabUnselected(TabLayout.Tab tab) { + } + + @Override + public void onTabReselected(TabLayout.Tab tab) { + } }); + // initial setup + final GroupChannelTotalUnreadMessageCountParams params = new GroupChannelTotalUnreadMessageCountParams(); + List customTypeFilter = new ArrayList<>(); + customTypeFilter.add(notificationChannelCustomType); + params.setChannelCustomTypes(customTypeFilter); + SendbirdChat.getTotalUnreadMessageCount(params, (notificationUnreadMessageCount, e) -> { + if (e != null) { + return; + } + Logger.i("SENDBIRD_NOTIFICATION_CHANNEL_NOTIFICATION unreadCount=%s", notificationUnreadMessageCount); + SendbirdChat.getTotalUnreadMessageCount(new GroupChannelTotalUnreadMessageCountParams(), (totalCount, e1) -> { + if (e1 != null) { + return; + } + Logger.i("updateChannelListTab totalCount=%s, unreadCount=%s", totalCount, notificationUnreadMessageCount); + drawUnreadCount(channelListTab, totalCount - notificationUnreadMessageCount); + if (!notificationTab.isSelected() || notificationUnreadMessageCount == 0) { + drawUnreadCount(notificationTab, notificationUnreadMessageCount); + } + }); + }); + + // It will call whenever reconnect has completed or real-time event that contains "unread_cnt" is coming. + // App attributes must be set + // 1. enable_admm_unread_count_payload: true + // 2. unread_cnt_subscription : all + // 2.1. custom_types (Register the custom type of the notification channel you are using in your app) SendbirdChat.addUserEventHandler(USER_EVENT_HANDLER_KEY, new UserEventHandler() { @Override - public void onFriendsDiscovered(@NonNull List list) {} + public void onFriendsDiscovered(@NonNull List list) { + } @Override public void onTotalUnreadMessageCountChanged(int totalCount, @NonNull Map totalCountByCustomType) { - if (totalCount > 0) { - unreadCountTab.setBadgeVisibility(View.VISIBLE); - unreadCountTab.setBadgeCount(totalCount > 99 ? - getString(R.string.text_tab_badge_max_count) : - String.valueOf(totalCount)); - } else { - unreadCountTab.setBadgeVisibility(View.GONE); + Integer notificationUnreadMessageCount = totalCountByCustomType.get(notificationChannelCustomType); + if (notificationUnreadMessageCount != null) { + Logger.i("updateChannelListTab totalCount=%s, unreadCount=%s", totalCount, notificationUnreadMessageCount); + drawUnreadCount(channelListTab, totalCount - notificationUnreadMessageCount); + if (!notificationTab.isSelected() || notificationUnreadMessageCount == 0) { + drawUnreadCount(notificationTab, notificationUnreadMessageCount); + } } } }); } + private synchronized void drawUnreadCount(@NonNull CustomTabView tabView, int count) { + if (count > 0) { + tabView.setBadgeVisibility(View.VISIBLE); + tabView.setBadgeCount(count > 99 ? getString(R.string.text_tab_badge_max_count) : String.valueOf(count)); + } else { + tabView.setBadgeVisibility(View.GONE); + } + } + @Override - protected void onPause() { - super.onPause(); + protected void onDestroy() { + super.onDestroy(); SendbirdChat.removeUserEventHandler(USER_EVENT_HANDLER_KEY); } @@ -123,10 +192,7 @@ protected void onNewIntent(@Nullable Intent intent) { } @NonNull - public static Intent newRedirectToChannelIntent( - @NonNull Context context, - @NonNull String channelUrl, - long messageId) { + public static Intent newRedirectToChannelIntent(@NonNull Context context, @NonNull String channelUrl, long messageId) { Intent intent = new Intent(context, GroupChannelMainActivity.class); intent.putExtra(PUSH_REDIRECT_CHANNEL, channelUrl); intent.putExtra(PUSH_REDIRECT_MESSAGE_ID, messageId); @@ -142,8 +208,13 @@ private void redirectChannelIfNeeded(Intent intent) { } if (intent.hasExtra(PUSH_REDIRECT_CHANNEL)) { - String channelUrl = intent.getStringExtra(PUSH_REDIRECT_CHANNEL); + final String channelUrl = intent.getStringExtra(PUSH_REDIRECT_CHANNEL); + intent.removeExtra(PUSH_REDIRECT_CHANNEL); if (channelUrl == null) return; + if (channelUrl.equals(getNotificationChannelUrl())) { + binding.vpMain.setCurrentItem(TAB_CHANNEL_NOTIFICATION); + return; + } if (intent.hasExtra(PUSH_REDIRECT_MESSAGE_ID)) { long messageId = intent.getLongExtra(PUSH_REDIRECT_MESSAGE_ID, 0L); @@ -154,30 +225,36 @@ private void redirectChannelIfNeeded(Intent intent) { } else { startActivity(ChannelActivity.newIntent(this, channelUrl)); } - intent.removeExtra(PUSH_REDIRECT_CHANNEL); } } private static class MainAdapter extends FragmentPagerAdapter { - private static final int PAGE_SIZE = 2; + private final List tabItems = new ArrayList<>(); public MainAdapter(@NonNull FragmentManager fm, int behavior) { super(fm, behavior); + final GroupChannelListQueryParams params = new GroupChannelListQueryParams(); + final List customTypesFilter = new ArrayList<>(); + customTypesFilter.add(""); + params.setCustomTypesFilter(customTypesFilter); + final ChannelListFragment channelListFragment = new ChannelListFragment.Builder() + .setGroupChannelListQuery(GroupChannel.createMyGroupChannelListQuery(params)) + .setUseHeader(true) + .build(); + tabItems.add(channelListFragment); + tabItems.add(SendbirdUIKit.getFragmentFactory().newNotificationChannelFragment(getNotificationChannelUrl(), new Bundle())); + tabItems.add(new SettingsFragment()); } @NonNull @Override public Fragment getItem(int position) { - if (position == 0) { - return SendbirdUIKit.getFragmentFactory().newChannelListFragment(new Bundle()); - } else { - return new SettingsFragment(); - } + return tabItems.get(position); } @Override public int getCount() { - return PAGE_SIZE; + return tabItems.size(); } } } diff --git a/uikit-sample/src/main/res/layout/activity_login.xml b/uikit-sample/src/main/res/layout/activity_login.xml index 2032b4ad..82e15323 100644 --- a/uikit-sample/src/main/res/layout/activity_login.xml +++ b/uikit-sample/src/main/res/layout/activity_login.xml @@ -63,6 +63,7 @@ android:hint="@string/text_hint_user_id" android:maxLines="1" android:layout_gravity="center_vertical" + android:digits="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_" android:background="@drawable/shape_rect_transparent" android:textSize="@dimen/sb_text_size_16" android:textCursorDrawable="@drawable/shape_cursor_drawable" @@ -125,4 +126,4 @@ - \ No newline at end of file + diff --git a/uikit-sample/src/main/res/values/strings.xml b/uikit-sample/src/main/res/values/strings.xml index 8394ed44..bb11fc50 100644 --- a/uikit-sample/src/main/res/values/strings.xml +++ b/uikit-sample/src/main/res/values/strings.xml @@ -19,6 +19,7 @@ Channels Settings + Notifications 99+ Home diff --git a/uikit/build.gradle b/uikit/build.gradle index eb5be8f8..063b8193 100644 --- a/uikit/build.gradle +++ b/uikit/build.gradle @@ -1,6 +1,7 @@ plugins { id 'com.android.library' id 'org.jetbrains.kotlin.android' + id 'kotlinx-serialization' } version = UIKIT_VERSION @@ -79,6 +80,7 @@ dependencies { implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2" } diff --git a/uikit/consumer-rules.pro b/uikit/consumer-rules.pro index 962f8c59..2dfca5ca 100644 --- a/uikit/consumer-rules.pro +++ b/uikit/consumer-rules.pro @@ -9,4 +9,50 @@ } -keep class com.bumptech.glide.load.data.ParcelFileDescriptorRewinder$InternalRewinder { *** rewind(); -} \ No newline at end of file +} + +# Kotlin Serialization +# Keep `Companion` object fields of serializable classes. +# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects. +-if @kotlinx.serialization.Serializable class ** +-keepclassmembers class com.sendbird.uikit.** { + static *** Companion; +} + +# Keep `serializer()` on companion objects (both default and named) of serializable classes. +-if @kotlinx.serialization.Serializable class ** { + static **$* *; +} +-keepclassmembers class com.sendbird.uikit.** { + kotlinx.serialization.KSerializer serializer(...); +} + +# Keep `INSTANCE.serializer()` of serializable objects. +-if @kotlinx.serialization.Serializable class ** { + public static ** INSTANCE; +} +-keepclassmembers class com.sendbird.uikit.** { + public static com.sendbird.uikit.** INSTANCE; + kotlinx.serialization.KSerializer serializer(...); +} + +# @Serializable and @Polymorphic are used at runtime for polymorphic serialization. +-keepattributes RuntimeVisibleAnnotations,AnnotationDefault + +-keepclassmembers class * { + *** writeReplace(); +} + +# Serializer for classes with named companion objects are retrieved using `getDeclaredClasses`. +# If you have any, uncomment and replace classes with those containing named companion objects. +#-keepattributes InnerClasses # Needed for `getDeclaredClasses`. +#-if @kotlinx.serialization.Serializable class +#com.example.myapplication.HasNamedCompanion, # <-- List serializable classes with named companions. +#com.example.myapplication.HasNamedCompanion2 +#{ +# static **$* *; +#} +#-keepnames class <1>$$serializer { # -keepnames suffices; class is kept when serializer() is kept. +# static <1>$$serializer INSTANCE; +#} + diff --git a/uikit/src/main/AndroidManifest.xml b/uikit/src/main/AndroidManifest.xml index fe16803b..34c64c71 100644 --- a/uikit/src/main/AndroidManifest.xml +++ b/uikit/src/main/AndroidManifest.xml @@ -131,6 +131,10 @@ android:name=".activities.OpenChannelActivity" android:configChanges="orientation|screenSize|keyboardHidden" android:windowSoftInputMode="adjustResize|stateHidden" /> + cls, @NonNull String channelUrl) { + Intent intent = new Intent(context, cls); + intent.putExtra(StringSet.KEY_CHANNEL_URL, channelUrl); + return intent; + } + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setTheme(SendbirdUIKit.isDarkMode() ? R.style.AppTheme_Dark_Sendbird : R.style.AppTheme_Sendbird); + setContentView(R.layout.sb_activity); + + String url = getIntent().getStringExtra(StringSet.KEY_CHANNEL_URL); + if (TextUtils.isEmpty(url)) { + ContextUtils.toastError(this, R.string.sb_text_error_get_channel); + } else { + Fragment fragment = createFragment(); + FragmentManager manager = getSupportFragmentManager(); + manager.popBackStack(); + manager.beginTransaction() + .replace(R.id.sb_fragment_container, fragment) + .commit(); + } + } + + /** + * It will be called when the {@link NotificationChannelActivity} is being created. + * The data contained in Intent is delivered to Fragment's Bundle. + * + * @return {@link com.sendbird.uikit.fragments.NotificationChannelFragment} + * @since 3.5.0 + */ + @NonNull + protected Fragment createFragment() { + final Bundle args = getIntent() != null && getIntent().getExtras() != null ? getIntent().getExtras() : new Bundle(); + return SendbirdUIKit.getFragmentFactory().newNotificationChannelFragment(args.getString(StringSet.KEY_CHANNEL_URL, ""), args); + } +} diff --git a/uikit/src/main/java/com/sendbird/uikit/activities/adapter/BaseMessageAdapter.java b/uikit/src/main/java/com/sendbird/uikit/activities/adapter/BaseMessageAdapter.java index d7d53f3e..85bc6256 100644 --- a/uikit/src/main/java/com/sendbird/uikit/activities/adapter/BaseMessageAdapter.java +++ b/uikit/src/main/java/com/sendbird/uikit/activities/adapter/BaseMessageAdapter.java @@ -2,17 +2,15 @@ import androidx.recyclerview.widget.RecyclerView; -import com.sendbird.uikit.activities.viewholder.MessageViewHolder; - import java.util.List; /** * BaseMessageAdapter provides a binding from an app-specific data set to views that are displayed within a RecyclerView. * * @param A class of data's type. - * @param A class that extends MessageViewHolder that will be used by the adapter. + * @param A class that extends RecyclerView.ViewHolder that will be used by the adapter. */ -abstract class BaseMessageAdapter extends RecyclerView.Adapter { +abstract class BaseMessageAdapter extends RecyclerView.Adapter { /** * Returns item that located given position. * diff --git a/uikit/src/main/java/com/sendbird/uikit/activities/adapter/MessageListAdapter.java b/uikit/src/main/java/com/sendbird/uikit/activities/adapter/MessageListAdapter.java index 3ce13c18..4742934f 100644 --- a/uikit/src/main/java/com/sendbird/uikit/activities/adapter/MessageListAdapter.java +++ b/uikit/src/main/java/com/sendbird/uikit/activities/adapter/MessageListAdapter.java @@ -10,7 +10,6 @@ * MessageListAdapter provides a binding from a {@link BaseMessage} type data set to views that are displayed within a RecyclerView. */ public class MessageListAdapter extends BaseMessageListAdapter { - public MessageListAdapter(boolean useMessageGroupUI) { this(null, useMessageGroupUI); } diff --git a/uikit/src/main/java/com/sendbird/uikit/activities/adapter/NotificationMessageListAdapter.java b/uikit/src/main/java/com/sendbird/uikit/activities/adapter/NotificationMessageListAdapter.java new file mode 100644 index 00000000..572e4513 --- /dev/null +++ b/uikit/src/main/java/com/sendbird/uikit/activities/adapter/NotificationMessageListAdapter.java @@ -0,0 +1,243 @@ +package com.sendbird.uikit.activities.adapter; + +import android.content.Context; +import android.util.TypedValue; +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.view.ContextThemeWrapper; +import androidx.recyclerview.widget.DiffUtil; +import androidx.recyclerview.widget.RecyclerView; + +import com.sendbird.android.channel.GroupChannel; +import com.sendbird.android.message.BaseMessage; +import com.sendbird.uikit.R; +import com.sendbird.uikit.SendbirdUIKit; +import com.sendbird.uikit.activities.viewholder.MessageType; +import com.sendbird.uikit.activities.viewholder.MessageViewHolderFactory; +import com.sendbird.uikit.databinding.SbViewMessageNotificationChannelBinding; +import com.sendbird.uikit.interfaces.OnMessageListUpdateHandler; +import com.sendbird.uikit.interfaces.OnMessageTemplateActionHandler; +import com.sendbird.uikit.internal.model.MessageTemplateDiffCallback; +import com.sendbird.uikit.internal.ui.viewholders.NotificationChannelMessageViewHolder; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * NotificationMessageListAdapter provides a binding from a {@link BaseMessage} type data set to views that are displayed within a RecyclerView. + */ +public class NotificationMessageListAdapter extends RecyclerView.Adapter { + @NonNull + private List messageList = new ArrayList<>(); + @NonNull + private GroupChannel channel; + @Nullable + private OnMessageTemplateActionHandler onMessageTemplateActionHandler; + private long prevLastSeenAt = 0L; + private long currentLastSeenAt = 0L; + private final boolean shouldDisplayUserProfile; + + // the worker must be a single thread. + @NonNull + private final ExecutorService differWorker = Executors.newSingleThreadExecutor(); + + /** + * Constructor + * + * @param channel The {@link GroupChannel} that contains the data needed for this adapter + * @since 3.5.0 + */ + public NotificationMessageListAdapter(@NonNull GroupChannel channel) { + this(channel, true); + } + + /** + * Constructor + * + * @param channel The {@link GroupChannel} that contains the data needed for this adapter + * @param shouldDisplayUserProfile true if the user profile is shown, false otherwise + * @since 3.5.0 + */ + public NotificationMessageListAdapter(@NonNull GroupChannel channel, boolean shouldDisplayUserProfile) { + this.channel = GroupChannel.clone(channel); + this.currentLastSeenAt = channel.getMyLastRead(); + this.shouldDisplayUserProfile = shouldDisplayUserProfile; + } + + /** + * Called when RecyclerView needs a new {@link NotificationChannelMessageViewHolder} of the given type to represent + * an item. + * + * @param parent The ViewGroup into which the new View will be added after it is bound to + * an adapter position. + * @param viewType The view type of the new View. + * @return A new {@link NotificationChannelMessageViewHolder} that holds a View of the given view type. + * @see #getItemViewType(int) + * @see #onBindViewHolder(NotificationChannelMessageViewHolder, int) + * @since 3.5.0 + */ + @NonNull + @Override + public NotificationChannelMessageViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + final TypedValue values = new TypedValue(); + parent.getContext().getTheme().resolveAttribute(R.attr.sb_component_list, values, true); + final Context contextWrapper = new ContextThemeWrapper(parent.getContext(), values.resourceId); + LayoutInflater inflater = LayoutInflater.from(contextWrapper); + return new NotificationChannelMessageViewHolder(SbViewMessageNotificationChannelBinding.inflate(inflater, parent, false), shouldDisplayUserProfile); + } + + /** + * Called by RecyclerView to display the data at the specified position. This method should + * update the contents of the {@link NotificationChannelMessageViewHolder#itemView} to reflect the item at the given + * position. + * + * @param holder The {@link NotificationChannelMessageViewHolder} which should be updated to represent + * the contents of the item at the given position in the data set. + * @param position The position of the item within the adapter's data set. + * @since 3.5.0 + */ + @Override + public void onBindViewHolder(@NonNull NotificationChannelMessageViewHolder holder, final int position) { + final BaseMessage message = getItem(position); + holder.setOnMessageTemplateActionHandler(onMessageTemplateActionHandler); + holder.bind(channel, message, currentLastSeenAt); + } + + /** + * Return the view type of the {@link NotificationChannelMessageViewHolder}. + * Notification channel always returns {@link MessageType#VIEW_TYPE_NOTIFICATION_CHANNEL_MESSAGE} + * + * @param position position to query + * @return integer value identifying the type of the view needed to represent the item at position. + * @see MessageViewHolderFactory#getViewType(BaseMessage) + * @since 3.5.0 + */ + @Override + public int getItemViewType(int position) { + return MessageType.VIEW_TYPE_NOTIFICATION_CHANNEL_MESSAGE.getValue(); + } + + /** + * Return ID for the message at position. + * + * @param position Adapter position to query + * @return the stable ID of the item at position + * @since 3.5.0 + */ + @Override + public long getItemId(int position) { + return getItem(position).getMessageId(); + } + + /** + * Sets the {@link List} to be displayed. + * + * @param messageList list to be displayed + * @since 3.5.0 + */ + public void setItems(@NonNull final GroupChannel channel, @NonNull final List messageList, @Nullable OnMessageListUpdateHandler callback) { + final GroupChannel copiedChannel = GroupChannel.clone(channel); + final List copiedMessage = Collections.unmodifiableList(messageList); + differWorker.submit(() -> { + final CountDownLatch lock = new CountDownLatch(1); + + final MessageTemplateDiffCallback diffCallback = new MessageTemplateDiffCallback( + NotificationMessageListAdapter.this.messageList, + messageList, + prevLastSeenAt, + currentLastSeenAt + ); + final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallback); + + SendbirdUIKit.runOnUIThread(() -> { + try { + NotificationMessageListAdapter.this.messageList = copiedMessage; + NotificationMessageListAdapter.this.channel = copiedChannel; + diffResult.dispatchUpdatesTo(NotificationMessageListAdapter.this); + if (callback != null) { + callback.onListUpdated(messageList); + } + } finally { + lock.countDown(); + } + }); + lock.await(); + return true; + }); + } + + /** + * Returns the total number of items in the data set held by the adapter. + * + * @return The total number of items in this adapter. + * @since 3.5.0 + */ + @Override + public int getItemCount() { + return messageList.size(); + } + + /** + * Returns the {@link BaseMessage} in the data set held by the adapter. + * + * @param position The position of the item within the adapter's data set. + * @return The {@link BaseMessage} to retrieve the position of in this adapter. + * @since 3.5.0 + */ + @NonNull + public BaseMessage getItem(int position) { + return messageList.get(position); + } + + /** + * Returns the {@link List} in the data set held by the adapter. + * + * @return The {@link List} in this adapter. + * @since 3.5.0 + */ + @NonNull + public List getItems() { + return Collections.unmodifiableList(messageList); + } + + /** + * Set the current user's last read timestamp in channel. + * + * @param lastSeenAt the current user's last read timestamp in channel. + * @since 3.5.0 + */ + public synchronized void updateLastSeenAt(long lastSeenAt) { + // set the previous lastSeenAt value due to compare the message changing status. + this.prevLastSeenAt = this.currentLastSeenAt; + this.currentLastSeenAt = lastSeenAt; + } + + /** + * Register a callback to be invoked when the view that has an {@link com.sendbird.uikit.model.Action} data is clicked. + * If an Action is registered in a specific view, it is called when a click event occurs. + * + * @param handler The callback that will run + * @since 3.5.0 + */ + public void setOnMessageTemplateActionHandler(@Nullable OnMessageTemplateActionHandler handler) { + this.onMessageTemplateActionHandler = handler; + } + + /** + * Returns a callback to be invoked when the view that has an {@link com.sendbird.uikit.model.Action} data is clicked. + * + * @return a callback to be invoked when the view that has an {@link com.sendbird.uikit.model.Action} data is clicked. + * @since 3.5.0 + */ + @Nullable + public OnMessageTemplateActionHandler getOnMessageTemplateActionHandler() { + return onMessageTemplateActionHandler; + } +} diff --git a/uikit/src/main/java/com/sendbird/uikit/activities/viewholder/MessageType.java b/uikit/src/main/java/com/sendbird/uikit/activities/viewholder/MessageType.java index db837169..5cc89cfa 100644 --- a/uikit/src/main/java/com/sendbird/uikit/activities/viewholder/MessageType.java +++ b/uikit/src/main/java/com/sendbird/uikit/activities/viewholder/MessageType.java @@ -60,7 +60,13 @@ public enum MessageType { * * @since 3.3.0 */ - VIEW_TYPE_PARENT_MESSAGE_INFO(12); + VIEW_TYPE_PARENT_MESSAGE_INFO(12), + /** + * Type of notification channel's message sent by the administrator. + * + * @since 3.5.0 + */ + VIEW_TYPE_NOTIFICATION_CHANNEL_MESSAGE(13); final int value; MessageType(int value) { diff --git a/uikit/src/main/java/com/sendbird/uikit/consts/StringSet.java b/uikit/src/main/java/com/sendbird/uikit/consts/StringSet.java index 7c07750c..5fc69b04 100644 --- a/uikit/src/main/java/com/sendbird/uikit/consts/StringSet.java +++ b/uikit/src/main/java/com/sendbird/uikit/consts/StringSet.java @@ -22,6 +22,7 @@ public class StringSet { public final static String KEY_DELETABLE_MESSAGE = "KEY_DELETABLE_MESSAGE"; public final static String KEY_USE_USER_PROFILE = "KEY_USE_USER_PROFILE"; + public final static String KEY_SHOULD_DISPLAY_USER_PROFILE = "KEY_SHOULD_DISPLAY_USER_PROFILE"; public final static String KEY_STARTING_POINT = "KEY_STARTING_POINT"; public final static String KEY_PARENT_MESSAGE = "KEY_PARENT_MESSAGE"; @@ -129,4 +130,10 @@ public class StringSet { public final static String ThreadInfo = "ThreadInfo"; public final static String ParentMessageMenu = "ParentMessageMenu"; public final static String _AT = "@"; + + // template message syntax + public final static String web = "web"; + public final static String custom = "custom"; + public final static String uikit = "uikit"; + public final static String delete = "delete"; } diff --git a/uikit/src/main/java/com/sendbird/uikit/fragments/ChannelFragment.java b/uikit/src/main/java/com/sendbird/uikit/fragments/ChannelFragment.java index ace17ed6..94230cb8 100644 --- a/uikit/src/main/java/com/sendbird/uikit/fragments/ChannelFragment.java +++ b/uikit/src/main/java/com/sendbird/uikit/fragments/ChannelFragment.java @@ -496,6 +496,7 @@ private void onInputRightButtonClicked(@NonNull View view) { params.setMentionedUsers(mentionedUsers); } } + sendUserMessage(params); } } diff --git a/uikit/src/main/java/com/sendbird/uikit/fragments/NotificationChannelFragment.java b/uikit/src/main/java/com/sendbird/uikit/fragments/NotificationChannelFragment.java new file mode 100644 index 00000000..fdcb4ab4 --- /dev/null +++ b/uikit/src/main/java/com/sendbird/uikit/fragments/NotificationChannelFragment.java @@ -0,0 +1,619 @@ +package com.sendbird.uikit.fragments; + +import android.content.ActivityNotFoundException; +import android.content.Intent; +import android.content.res.ColorStateList; +import android.net.Uri; +import android.os.Bundle; +import android.view.View; + +import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; +import androidx.annotation.StyleRes; +import androidx.lifecycle.ViewModelProvider; + +import com.sendbird.android.channel.GroupChannel; +import com.sendbird.android.message.BaseMessage; +import com.sendbird.android.params.MessageListParams; +import com.sendbird.uikit.R; +import com.sendbird.uikit.SendbirdUIKit; +import com.sendbird.uikit.activities.adapter.NotificationMessageListAdapter; +import com.sendbird.uikit.consts.StringSet; +import com.sendbird.uikit.interfaces.LoadingDialogHandler; +import com.sendbird.uikit.interfaces.OnMessageTemplateActionHandler; +import com.sendbird.uikit.log.Logger; +import com.sendbird.uikit.model.Action; +import com.sendbird.uikit.model.ReadyStatus; +import com.sendbird.uikit.modules.NotificationChannelModule; +import com.sendbird.uikit.modules.components.HeaderComponent; +import com.sendbird.uikit.modules.components.NotificationMessageListComponent; +import com.sendbird.uikit.modules.components.StatusComponent; +import com.sendbird.uikit.utils.DialogUtils; +import com.sendbird.uikit.utils.IntentUtils; +import com.sendbird.uikit.utils.TextUtils; +import com.sendbird.uikit.vm.NotificationChannelViewModel; +import com.sendbird.uikit.vm.ViewModelFactory; + +public class NotificationChannelFragment extends BaseModuleFragment { + + @Nullable + private View.OnClickListener headerLeftButtonClickListener; + @Nullable + private LoadingDialogHandler loadingDialogHandler; + @Nullable + private NotificationMessageListAdapter adapter; + @Nullable + private OnMessageTemplateActionHandler actionHandler; + @Nullable + private MessageListParams params; + + @NonNull + @Override + protected NotificationChannelModule onCreateModule(@NonNull Bundle args) { + return new NotificationChannelModule(requireContext()); + } + + @Override + protected void onConfigureParams(@NonNull NotificationChannelModule module, @NonNull Bundle args) { + if (loadingDialogHandler != null) module.setOnLoadingDialogHandler(loadingDialogHandler); + } + + @NonNull + @Override + protected NotificationChannelViewModel onCreateViewModel() { + final NotificationChannelViewModel viewModel = new ViewModelProvider(this, new ViewModelFactory(getChannelUrl(), params)).get(getChannelUrl(), NotificationChannelViewModel.class); + getLifecycle().addObserver(viewModel); + return viewModel; + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + shouldShowLoadingDialog(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + shouldDismissLoadingDialog(); + } + + @Override + protected void onBeforeReady(@NonNull ReadyStatus status, @NonNull NotificationChannelModule module, @NonNull NotificationChannelViewModel viewModel) { + Logger.d(">> NotificationChannelFragment::onBeforeReady status=%s", status); + module.getMessageListComponent().setPagedDataLoader(viewModel); + if (this.adapter != null) { + module.getMessageListComponent().setAdapter(adapter); + } + final GroupChannel channel = viewModel.getChannel(); + onBindHeaderComponent(module.getHeaderComponent(), viewModel, channel); + onBindNotificationMessageListComponent(module.getMessageListComponent(), viewModel, channel); + onBindStatusComponent(module.getStatusComponent(), viewModel, channel); + } + + @Override + protected void onReady(@NonNull ReadyStatus status, @NonNull NotificationChannelModule module, @NonNull NotificationChannelViewModel viewModel) { + Logger.d(">> NotificationChannelFragment::onReady status=%s", status); + shouldDismissLoadingDialog(); + final GroupChannel channel = viewModel.getChannel(); + if (status == ReadyStatus.ERROR || channel == null) { + if (isFragmentAlive()) { + toastError(R.string.sb_text_error_get_channel); + shouldActivityFinish(); + } + return; + } + + module.getMessageListComponent().notifyChannelChanged(channel); + viewModel.onChannelDeleted().observe(getViewLifecycleOwner(), channelUrl -> shouldActivityFinish()); + loadInitial(); + } + + /** + * Called to bind events to the HeaderComponent. This is called from {@link #onBeforeReady(ReadyStatus, NotificationChannelModule, NotificationChannelViewModel)} regardless of the value of {@link ReadyStatus}. + * + * @param headerComponent The component to which the event will be bound + * @param viewModel A view model that provides the data needed for the fragment + * @param channel The {@code GroupChannel} that contains the data needed for this fragment + * @since 3.5.0 + */ + protected void onBindHeaderComponent(@NonNull HeaderComponent headerComponent, @NonNull NotificationChannelViewModel viewModel, @Nullable GroupChannel channel) { + Logger.d(">> NotificationChannelFragment::onBindNotificationHeaderComponent()"); + headerComponent.setOnLeftButtonClickListener(headerLeftButtonClickListener != null ? headerLeftButtonClickListener : v -> shouldActivityFinish()); + } + + /** + * Called to bind events to the NotificationMessageListComponent. This is called from {@link #onBeforeReady(ReadyStatus, NotificationChannelModule, NotificationChannelViewModel)} regardless of the value of {@link ReadyStatus}. + * + * @param listComponent The component to which the event will be bound + * @param viewModel A view model that provides the data needed for the fragment + * @param channel The {@code GroupChannel} that contains the data needed for this fragment + * @since 3.5.0 + */ + protected void onBindNotificationMessageListComponent(@NonNull NotificationMessageListComponent listComponent, @NonNull NotificationChannelViewModel viewModel, @Nullable GroupChannel channel) { + Logger.d(">> NotificationChannelFragment::onBindNotificationMessageListComponent()"); + listComponent.setOnMessageTemplateActionHandler(actionHandler != null ? actionHandler : this::handleAction); + viewModel.getMessageList().observeAlways(getViewLifecycleOwner(), messageData -> { + if (!isFragmentAlive() || channel == null) return; + listComponent.notifyDataSetChanged(messageData.getMessages(), channel, null); + }); + } + + /** + * Called to bind events to the StatusComponent. This is called from {@link #onBeforeReady(ReadyStatus, NotificationChannelModule, NotificationChannelViewModel)} regardless of the value of {@link ReadyStatus}. + * + * @param statusComponent The component to which the event will be bound + * @param viewModel A view model that provides the data needed for the fragment + * @param channel The {@code GroupChannel} that contains the data needed for this fragment + * @since 3.5.0 + */ + protected void onBindStatusComponent(@NonNull StatusComponent statusComponent, @NonNull NotificationChannelViewModel viewModel, @Nullable GroupChannel channel) { + Logger.d(">> NotificationChannelFragment::onBindStatusComponent()"); + viewModel.getStatusFrame().observe(getViewLifecycleOwner(), statusComponent::notifyStatusChanged); + } + + private void handleAction(@NonNull View view, @NonNull Action action, @NonNull BaseMessage message) { + switch (action.getType()) { + case StringSet.web: + handleWebAction(view, action, message); + break; + case StringSet.custom: + handleCustomAction(view, action, message); + break; + case StringSet.uikit: + handlePredefinedAction(view, action, message); + break; + default: + break; + } + } + + /** + * If an Action is registered in a specific view, it is called when a click event occurs. + * + * @param action the registered Action data + * @param message a clicked message + * @since 3.5.0 + */ + protected void handleWebAction(@NonNull View view, @NonNull Action action, @NonNull BaseMessage message) { + Logger.d(">> NotificationChannelFragment::handleWebAction() action=%s", action); + final Intent intent = IntentUtils.getWebViewerIntent(action.getData()); + try { + startActivity(intent); + } catch (ActivityNotFoundException e) { + Logger.e(e); + } + } + + /** + * If an Action is registered in a specific view, it is called when a click event occurs. + * + * @param action the registered Action data + * @param message a clicked message + * @since 3.5.0 + */ + protected void handleCustomAction(@NonNull View view, @NonNull Action action, @NonNull BaseMessage message) { + Logger.d(">> NotificationChannelFragment::handleCustomAction() action=%s", action); + try { + final String data = action.getData(); + if (TextUtils.isNotEmpty(data)) { + final Uri uri = Uri.parse(data); + Logger.d("++ uri = %s", uri); + final String scheme = uri.getScheme(); + Logger.d("++ scheme=%s", scheme); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + boolean hasIntent = IntentUtils.hasIntent(requireContext(), intent); + if (!hasIntent) { + final String alterData = action.getAlterData(); + intent = new Intent(Intent.ACTION_VIEW, Uri.parse(alterData)); + } + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + } + } catch (Exception e) { + Logger.w(e); + } + } + + /** + * If an Action is registered in a specific view, it is called when a click event occurs. + * + * + *
    + * Support below actions. + *
  • delete: Request deleting message. (e.g: sendbirduikit://delete) + *
+ * + * @param action the registered Action data + * @param message a clicked message + * @since 3.5.0 + */ + protected void handlePredefinedAction(@NonNull View view, @NonNull Action action, @NonNull BaseMessage message) { + Logger.d(">> NotificationChannelFragment::handleUIKitAction() action=%s", action); + final String data = action.getData(); + if (TextUtils.isNotEmpty(data)) { + final Uri uri = Uri.parse(data); + Logger.d("++ uri = %s", uri); + final String host = uri.getHost(); + final String path = uri.getPath(); + Logger.d("++ host=%s, path=%s",host, path); + if (StringSet.delete.equals(host)) { + showWarningDialog(message); + } + } + } + + /** + * Request refreshing the message list. + * Renews the channel and updates the last read time value together. + * + * @since 3.5.0 + */ + public void updateLastReadTimeOnCurrentChannel() { + Logger.d(">> NotificationChannelFragment::updateLastReadTimeOnCurrentChannel()"); + if (!isFragmentAlive()) return; + final GroupChannel channel = getViewModel().getChannel(); + if (channel != null) { + final NotificationMessageListComponent listComponent = getModule().getMessageListComponent(); + listComponent.notifyLastSeenUpdated(getViewModel().getChannel().getMyLastRead()); + } + } + + private void showWarningDialog(@NonNull BaseMessage message) { + if (getContext() == null) return; + DialogUtils.showWarningDialog( + requireContext(), + getString(R.string.sb_text_dialog_delete_message), + getString(R.string.sb_text_button_delete), + delete -> { + Logger.dev("delete"); + deleteMessage(message); + }, + getString(R.string.sb_text_button_cancel), + cancel -> Logger.dev("cancel")); + } + + /** + * Delete a message + * + * @param message Message to delete. + * @since 3.5.0 + */ + protected void deleteMessage(@NonNull BaseMessage message) { + getViewModel().deleteMessage(message, e -> { + if (e != null) toastError(R.string.sb_text_error_delete_message); + }); + } + + /** + * It will be called when the loading dialog needs displaying. + * + * @return True if the callback has consumed the event, false otherwise. + * @since 3.5.0 + */ + protected boolean shouldShowLoadingDialog() { + return getModule().shouldShowLoadingDialog(); + } + + /** + * It will be called when the loading dialog needs dismissing. + * + * @since 3.5.0 + */ + protected void shouldDismissLoadingDialog() { + getModule().shouldDismissLoadingDialog(); + } + + private synchronized void loadInitial() { + getViewModel().loadInitial(Long.MAX_VALUE); + } + + /** + * Returns the URL of the channel with the required data to use this fragment. + * + * @return The URL of a channel this fragment is currently associated with + * @since 3.5.0 + */ + @NonNull + protected String getChannelUrl() { + final Bundle args = getArguments() == null ? new Bundle() : getArguments(); + return args.getString(StringSet.KEY_CHANNEL_URL, ""); + } + + public static class Builder { + @NonNull + private final Bundle bundle; + @Nullable + private NotificationChannelFragment customFragment; + @Nullable + private View.OnClickListener headerLeftButtonClickListener; + @Nullable + private NotificationMessageListAdapter adapter; + @Nullable + private LoadingDialogHandler loadingDialogHandler; + @Nullable + private OnMessageTemplateActionHandler actionHandler; + @Nullable + private MessageListParams params; + + /** + * Constructor + * + * @param channelUrl the url of the channel will be implemented. + * @since 3.5.0 + */ + public Builder(@NonNull String channelUrl) { + this(channelUrl, SendbirdUIKit.getDefaultThemeMode()); + } + + /** + * Constructor + * + * @param channelUrl the url of the channel will be implemented. + * @param themeMode {@link SendbirdUIKit.ThemeMode} + * @since 3.5.0 + */ + public Builder(@NonNull String channelUrl, @NonNull SendbirdUIKit.ThemeMode themeMode) { + this(channelUrl, themeMode.getResId()); + } + + /** + * Constructor + * + * @param channelUrl the url of the channel will be implemented. + * @param customThemeResId the resource identifier for custom theme. + * @since 3.5.0 + */ + public Builder(@NonNull String channelUrl, @StyleRes int customThemeResId) { + bundle = new Bundle(); + bundle.putInt(StringSet.KEY_THEME_RES_ID, customThemeResId); + bundle.putString(StringSet.KEY_CHANNEL_URL, channelUrl); + } + + /** + * Sets arguments to this fragment. + * + * @param args the arguments supplied when the fragment was instantiated. + * @return This Builder object to allow for chaining of calls to set methods. + * @since 3.5.0 + */ + @NonNull + public Builder withArguments(@NonNull Bundle args) { + this.bundle.putAll(args); + return this; + } + + /** + * Sets the title of the header. + * + * @param title text to be displayed. + * @return This Builder object to allow for chaining of calls to set methods. + * @since 3.5.0 + */ + @NonNull + public Builder setHeaderTitle(@NonNull String title) { + bundle.putString(StringSet.KEY_HEADER_TITLE, title); + return this; + } + + /** + * Sets the click listener on the message template view clicked. + * Sets the click listener when the view component that has {@link com.sendbird.uikit.model.Action} is clicked + * + * @param handler The callback that will run. + * @return This Builder object to allow for chaining of calls to set methods. + * @since 3.5.0 + */ + @NonNull + public Builder setOnMessageTemplateActionHandler(@NonNull OnMessageTemplateActionHandler handler) { + this.actionHandler = handler; + return this; + } + + /** + * Sets whether the header is used. + * + * @param useHeader true if the header is used, false otherwise. + * @return This Builder object to allow for chaining of calls to set methods. + * @since 3.5.0 + */ + @NonNull + public Builder setUseHeader(boolean useHeader) { + bundle.putBoolean(StringSet.KEY_USE_HEADER, useHeader); + return this; + } + + /** + * Sets whether to display the user profile when drawing a message. + * + * @param shouldDisplayUserProfile true if the user profile is shown, false otherwise + * @return This Builder object to allow for chaining of calls to set methods. + * @since 3.5.0 + */ + @NonNull + public Builder setDisplayUserProfile(boolean shouldDisplayUserProfile) { + bundle.putBoolean(StringSet.KEY_SHOULD_DISPLAY_USER_PROFILE, shouldDisplayUserProfile); + return this; + } + + /** + * Sets whether the left button of the header is used. + * + * @param useHeaderLeftButton true if the left button of the header is used, + * false otherwise. + * @return This Builder object to allow for chaining of calls to set methods. + * @since 3.5.0 + */ + @NonNull + public Builder setUseHeaderLeftButton(boolean useHeaderLeftButton) { + bundle.putBoolean(StringSet.KEY_USE_HEADER_LEFT_BUTTON, useHeaderLeftButton); + return this; + } + + /** + * Sets the icon on the left button of the header. + * + * @param resId the resource identifier of the drawable. + * @return This Builder object to allow for chaining of calls to set methods. + * @since 3.5.0 + */ + @NonNull + public Builder setHeaderLeftButtonIconResId(@DrawableRes int resId) { + return setHeaderLeftButtonIcon(resId, null); + } + + /** + * Sets the icon on the left button of the header. + * + * @param resId the resource identifier of the drawable. + * @param tint Color state list to use for tinting this resource, or null to clear the tint. + * @return This Builder object to allow for chaining of calls to set methods. + * @since 3.5.0 + */ + @NonNull + public Builder setHeaderLeftButtonIcon(@DrawableRes int resId, @Nullable ColorStateList tint) { + bundle.putInt(StringSet.KEY_HEADER_LEFT_BUTTON_ICON_RES_ID, resId); + bundle.putParcelable(StringSet.KEY_HEADER_LEFT_BUTTON_ICON_TINT, tint); + return this; + } + + /** + * Sets the click listener on the left button of the header. + * + * @param listener The callback that will run. + * @return This Builder object to allow for chaining of calls to set methods. + * @since 3.5.0 + */ + @NonNull + public Builder setOnHeaderLeftButtonClickListener(@NonNull View.OnClickListener listener) { + this.headerLeftButtonClickListener = listener; + return this; + } + + /** + * Sets the custom fragment. It must inherit {@link NotificationChannelFragment}. + * + * @param fragment custom fragment. + * @return This Builder object to allow for chaining of calls to set methods. + * @since 3.5.0 + */ + @NonNull + public Builder setCustomFragment(T fragment) { + this.customFragment = fragment; + return this; + } + + /** + * Sets the icon when the data is not exists. + * + * @param resId the resource identifier of the drawable. + * @return This Builder object to allow for chaining of calls to set methods. + * @since 3.5.0 + */ + @NonNull + public Builder setEmptyIcon(@DrawableRes int resId) { + return setEmptyIcon(resId, null); + } + + /** + * Sets the icon when the data is not exists. + * + * @param resId the resource identifier of the drawable. + * @param tint Color state list to use for tinting this resource, or null to clear the tint. + * @return This Builder object to allow for chaining of calls to set methods. + * @since 3.5.0 + */ + @NonNull + public Builder setEmptyIcon(@DrawableRes int resId, @Nullable ColorStateList tint) { + bundle.putInt(StringSet.KEY_EMPTY_ICON_RES_ID, resId); + bundle.putParcelable(StringSet.KEY_EMPTY_ICON_TINT, tint); + return this; + } + + /** + * Sets the text when the data is not exists + * + * @param resId the resource identifier of text to be displayed. + * @return This Builder object to allow for chaining of calls to set methods. + * @since 3.5.0 + */ + @NonNull + public Builder setEmptyText(@StringRes int resId) { + bundle.putInt(StringSet.KEY_EMPTY_TEXT_RES_ID, resId); + return this; + } + + /** + * Sets the text when error occurs + * + * @param resId the resource identifier of text to be displayed. + * @return This Builder object to allow for chaining of calls to set methods. + * @since 3.5.0 + */ + @NonNull + public Builder setErrorText(@StringRes int resId) { + bundle.putInt(StringSet.KEY_ERROR_TEXT_RES_ID, resId); + return this; + } + + /** + * Sets the notification channel adapter. + * + * @param adapter the adapter for the notification channel. + * @return This Builder object to allow for chaining of calls to set methods. + * @since 3.5.0 + */ + @NonNull + public Builder setNotificationMessageAdapter(T adapter) { + this.adapter = adapter; + return this; + } + + /** + * Sets the custom loading dialog handler + * + * @param loadingDialogHandler Interface definition for a callback to be invoked before when the loading dialog is called. + * @see LoadingDialogHandler + * @since 3.5.0 + */ + @NonNull + public Builder setLoadingDialogHandler(@NonNull LoadingDialogHandler loadingDialogHandler) { + this.loadingDialogHandler = loadingDialogHandler; + return this; + } + + /** + * Sets the message list params for this channel. + * The reverse and the nextResultSize properties in the MessageListParams are used in the UIKit. Even though you set that property it will be ignored. + * + * @param params The MessageListParams instance that you want to use. + * @return This Builder object to allow for chaining of calls to set methods. + * @since 3.5.0 + */ + @NonNull + public Builder setMessageListParams(@NonNull MessageListParams params) { + this.params = params; + return this; + } + + /** + * Creates an {@link NotificationChannelFragment} with the arguments supplied to this + * builder. + * + * @return The {@link NotificationChannelFragment} applied to the {@link Bundle}. + * @since 3.5.0 + */ + @NonNull + public NotificationChannelFragment build() { + NotificationChannelFragment fragment = customFragment != null ? customFragment : new NotificationChannelFragment(); + fragment.setArguments(bundle); + fragment.loadingDialogHandler = loadingDialogHandler; + fragment.headerLeftButtonClickListener = headerLeftButtonClickListener; + fragment.adapter = adapter; + fragment.params = params; + fragment.actionHandler = actionHandler; + return fragment; + } + } +} diff --git a/uikit/src/main/java/com/sendbird/uikit/fragments/UIKitFragmentFactory.java b/uikit/src/main/java/com/sendbird/uikit/fragments/UIKitFragmentFactory.java index 2940deb5..84b5dadd 100644 --- a/uikit/src/main/java/com/sendbird/uikit/fragments/UIKitFragmentFactory.java +++ b/uikit/src/main/java/com/sendbird/uikit/fragments/UIKitFragmentFactory.java @@ -397,4 +397,18 @@ public Fragment newMessageThreadFragment(@NonNull String channelUrl, @NonNull Ba .withArguments(args) .build(); } + + /** + * Returns the NotificationChannelFragment. + * + * @param args the arguments supplied when the fragment was instantiated. + * @return The {@link NotificationChannelFragment} + * @since 3.5.0 + */ + @NonNull + public Fragment newNotificationChannelFragment(@NonNull String channelUrl, @NonNull Bundle args) { + return new NotificationChannelFragment.Builder(channelUrl) + .withArguments(args) + .build(); + } } diff --git a/uikit/src/main/java/com/sendbird/uikit/interfaces/OnMessageTemplateActionHandler.java b/uikit/src/main/java/com/sendbird/uikit/interfaces/OnMessageTemplateActionHandler.java new file mode 100644 index 00000000..d060f42a --- /dev/null +++ b/uikit/src/main/java/com/sendbird/uikit/interfaces/OnMessageTemplateActionHandler.java @@ -0,0 +1,25 @@ +package com.sendbird.uikit.interfaces; + +import android.view.View; + +import androidx.annotation.NonNull; + +import com.sendbird.android.message.BaseMessage; +import com.sendbird.uikit.model.Action; + +/** + * Interface definition for a callback to be invoked when a item is invoked with an event. + * + * @since 3.5.0 + */ +public interface OnMessageTemplateActionHandler { + /** + * If an Action is registered in a specific view, it is called when a click event occurs. + * + * @param view the view that was clicked. + * @param action the registered Action data + * @param message the clicked message + * @since 3.5.0 + */ + void onHandleAction(@NonNull View view, @NonNull Action action, @NonNull BaseMessage message); +} diff --git a/uikit/src/main/java/com/sendbird/uikit/internal/MessageTemplateParser.kt b/uikit/src/main/java/com/sendbird/uikit/internal/MessageTemplateParser.kt new file mode 100644 index 00000000..8d139e37 --- /dev/null +++ b/uikit/src/main/java/com/sendbird/uikit/internal/MessageTemplateParser.kt @@ -0,0 +1,78 @@ +package com.sendbird.uikit.internal + +import com.sendbird.android.message.BaseMessage +import com.sendbird.uikit.internal.model.template_messages.Body +import com.sendbird.uikit.internal.model.template_messages.KeySet +import com.sendbird.uikit.internal.model.template_messages.Padding +import com.sendbird.uikit.internal.model.template_messages.Params +import com.sendbird.uikit.internal.model.template_messages.TextStyle +import com.sendbird.uikit.internal.model.template_messages.TextViewParams +import com.sendbird.uikit.internal.model.template_messages.ViewStyle +import com.sendbird.uikit.internal.model.template_messages.ViewType +import com.sendbird.uikit.log.Logger +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.decodeFromJsonElement +import org.json.JSONObject + +internal object MessageTemplateParser { + private val json by lazy { + Json { + prettyPrint = true + ignoreUnknownKeys = true + + // https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/json.md#coercing-input-values + // coerceInputValues = true + + // https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/json.md#encoding-defaults + // encodeDefaults = true + } + } + + @JvmStatic + fun parseParams(el: JsonElement): Params { + return json.decodeFromJsonElement(el) + } + + @JvmStatic + fun parseParams(jsonStr: String): Params { + return json.decodeFromString(jsonStr) + } + + @JvmStatic + fun parseParams(message: BaseMessage, defaultErrorMessage: String = ""): Params { + return try { + val subData: String = message.extendedMessage[KeySet.sub_data] ?: "" + when (val version = JSONObject(subData).getInt(KeySet.version)) { + 1 -> parseParams(subData) + else -> throw RuntimeException("unsupported version. current version = $version") + } + } catch (e: Throwable) { + Logger.e("$e, data=${message.extendedMessage[KeySet.sub_data] ?: ""}") + createDefaultViewParam(message, defaultErrorMessage) + } + } + + private fun createDefaultViewParam(message: BaseMessage, defaultErrorMessage: String): Params { + return Params( + version = 1, + body = Body( + items = listOf( + TextViewParams( + type = ViewType.Text, + viewStyle = ViewStyle( + padding = Padding( + 12, 12, 12, 12 + ), + ), + textStyle = TextStyle( + size = 14 + ), + text = message.message.takeIf { it.isNotEmpty() } ?: defaultErrorMessage + ) + ) + ) + ) + } +} diff --git a/uikit/src/main/java/com/sendbird/uikit/internal/RoundableView.kt b/uikit/src/main/java/com/sendbird/uikit/internal/RoundableView.kt new file mode 100644 index 00000000..b939a76f --- /dev/null +++ b/uikit/src/main/java/com/sendbird/uikit/internal/RoundableView.kt @@ -0,0 +1,10 @@ +package com.sendbird.uikit.internal + +import android.graphics.Color +import androidx.annotation.ColorInt + +internal interface RoundableView { + var radius: Float + fun setRadiusIntSize(radius: Int) + fun setBorder(borderWidth: Int = 0, @ColorInt borderColor: Int = Color.TRANSPARENT) +} diff --git a/uikit/src/main/java/com/sendbird/uikit/internal/extensions/MessageExtensions.kt b/uikit/src/main/java/com/sendbird/uikit/internal/extensions/MessageExtensions.kt index ff26c221..03f272fb 100644 --- a/uikit/src/main/java/com/sendbird/uikit/internal/extensions/MessageExtensions.kt +++ b/uikit/src/main/java/com/sendbird/uikit/internal/extensions/MessageExtensions.kt @@ -2,4 +2,4 @@ package com.sendbird.uikit.internal.extensions import com.sendbird.android.message.BaseMessage -fun BaseMessage.hasParentMessage() = parentMessageId != 0L +internal fun BaseMessage.hasParentMessage() = parentMessageId != 0L diff --git a/uikit/src/main/java/com/sendbird/uikit/internal/extensions/Resources.kt b/uikit/src/main/java/com/sendbird/uikit/internal/extensions/Resources.kt new file mode 100644 index 00000000..d17e9839 --- /dev/null +++ b/uikit/src/main/java/com/sendbird/uikit/internal/extensions/Resources.kt @@ -0,0 +1,8 @@ +package com.sendbird.uikit.internal.extensions + +import android.content.res.Resources + +// int value is a pure number value. For example it makes 10 and 10DP equal. +internal fun Resources.intToDp(value: Int): Int { + return (value * displayMetrics.density + 0.5f).toInt() +} diff --git a/uikit/src/main/java/com/sendbird/uikit/internal/extensions/ViewExtensions.kt b/uikit/src/main/java/com/sendbird/uikit/internal/extensions/ViewExtensions.kt index 91d1cf86..f49f6f60 100644 --- a/uikit/src/main/java/com/sendbird/uikit/internal/extensions/ViewExtensions.kt +++ b/uikit/src/main/java/com/sendbird/uikit/internal/extensions/ViewExtensions.kt @@ -2,15 +2,26 @@ package com.sendbird.uikit.internal.extensions import android.annotation.SuppressLint import android.content.Context +import android.content.res.ColorStateList import android.graphics.drawable.Drawable +import android.graphics.drawable.RippleDrawable import android.os.Build +import android.util.TypedValue +import android.view.View import android.widget.EditText +import android.widget.ImageView import android.widget.TextView import androidx.core.content.ContextCompat +import com.bumptech.glide.Glide +import com.bumptech.glide.load.engine.DiskCacheStrategy +import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.bumptech.glide.request.RequestOptions +import com.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition import com.sendbird.uikit.consts.StringSet @Suppress("DEPRECATION") -fun TextView.setAppearance(context: Context, res: Int) { +internal fun TextView.setAppearance(context: Context, res: Int) { if (Build.VERSION.SDK_INT < 23) { setTextAppearance(context, res) } else { @@ -18,14 +29,14 @@ fun TextView.setAppearance(context: Context, res: Int) { } } -fun EditText.setCursorDrawable(context: Context, res: Int) { +internal fun EditText.setCursorDrawable(context: Context, res: Int) { ContextCompat.getDrawable(context, res)?.let { setCursorDrawable(it) } } @SuppressLint("DiscouragedPrivateApi") -fun EditText.setCursorDrawable(cursor: Drawable) { +internal fun EditText.setCursorDrawable(cursor: Drawable) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { textCursorDrawable = cursor } else { @@ -37,3 +48,56 @@ fun EditText.setCursorDrawable(cursor: Drawable) { } } } + +internal fun View.loadToBackground(url: String, radius: Int = 0, useRipple: Boolean = false) { + var builder = Glide.with(this) + .asDrawable() + .load(url) + .diskCacheStrategy(DiskCacheStrategy.ALL) + if (radius > 0) { + builder = builder.apply(RequestOptions().transform(RoundedCorners(context.resources.intToDp(radius)))) + } + builder.into(object : CustomTarget() { + override fun onResourceReady(resource: Drawable, transition: Transition?) { + if (useRipple) this@loadToBackground.addRipple(resource) else background = resource + } + + override fun onLoadCleared(placeholder: Drawable?) { + } + }) +} + +internal fun ImageView.load(url: String) { + Glide.with(this) + .asDrawable() + .load(url) + // If the height of the image sets as a warp, it needs to be set to a specific size because it is unnatural when scrolling.(with adjustViewBounds true) + .diskCacheStrategy(DiskCacheStrategy.ALL) + .into(this) +} + +internal fun ImageView.load(url: String, resizingSize: Pair?) { + var request = Glide.with(this) + .asDrawable() + .diskCacheStrategy(DiskCacheStrategy.ALL) + resizingSize?.let { + if (it.first > 0 && it.second > 0) { + request = request.override(it.first, it.second) + } + } + request.load(url).into(this) +} + +internal fun View.addRipple(background: Drawable?) = with(TypedValue()) { + context.theme.resolveAttribute(android.R.attr.colorControlHighlight, this, true) + val color = ContextCompat.getColor(context, resourceId) + this@addRipple.background = createRippleDrawable(color, background) +} + +private fun createRippleDrawable(pressedColor: Int, backgroundDrawable: Drawable?): RippleDrawable { + return RippleDrawable(getPressedState(pressedColor), backgroundDrawable, null) +} + +private fun getPressedState(pressedColor: Int): ColorStateList { + return ColorStateList(arrayOf(intArrayOf()), intArrayOf(pressedColor)) +} diff --git a/uikit/src/main/java/com/sendbird/uikit/internal/model/MessageTemplateDiffCallback.kt b/uikit/src/main/java/com/sendbird/uikit/internal/model/MessageTemplateDiffCallback.kt new file mode 100644 index 00000000..9afd0f5e --- /dev/null +++ b/uikit/src/main/java/com/sendbird/uikit/internal/model/MessageTemplateDiffCallback.kt @@ -0,0 +1,81 @@ +package com.sendbird.uikit.internal.model + +import androidx.recyclerview.widget.DiffUtil +import com.sendbird.android.message.BaseMessage + +internal class MessageTemplateDiffCallback( + private val oldMessageList: List, + private val newMessageList: List, + private val oldLastSeenAt: Long, + private val newLastSeenAt: Long +) : DiffUtil.Callback() { + /** + * Returns the size of the old list. + * + * @return The size of the old list. + */ + override fun getOldListSize(): Int = oldMessageList.size + + /** + * Returns the size of the new list. + * + * @return The size of the new list. + */ + override fun getNewListSize(): Int = newMessageList.size + + /** + * Called by the DiffUtil to decide whether two object represent the same Item. + * + * + * For example, if your items have unique ids, this method should check their id equality. + * + * @param oldItemPosition The position of the item in the old list + * @param newItemPosition The position of the item in the new list + * @return True if the two items represent the same object or false if they are different. + */ + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + val oldMessage = oldMessageList[oldItemPosition] + val newMessage = newMessageList[newItemPosition] + return oldMessage.messageId == newMessage.messageId + } + + /** + * Called by the DiffUtil when it wants to check whether two items have the same data. + * DiffUtil uses this information to detect if the contents of an item has changed. + * + * + * DiffUtil uses this method to check equality instead of [Object.equals] + * so that you can change its behavior depending on your UI. + * For example, if you are using DiffUtil with a + * [RecyclerView.Adapter], you should + * return whether the items' visual representations are the same. + * + * + * This method is called only if [.areItemsTheSame] returns + * `true` for these items. + * + * @param oldItemPosition The position of the item in the old list + * @param newItemPosition The position of the item in the new list which replaces the + * oldItem + * @return True if the contents of the items are the same or false if they are different. + */ + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + val oldMessage = oldMessageList[oldItemPosition] + val newMessage = newMessageList[newItemPosition] + + if (oldMessage.customType != newMessage.customType) { + return false + } + + if (oldMessage.createdAt != newMessage.createdAt) { + return false + } + + val prevIsNew: Boolean = oldMessage.createdAt > oldLastSeenAt + val currentIsNew: Boolean = newMessage.createdAt > newLastSeenAt + if (prevIsNew != currentIsNew) { + return false + } + return true + } +} diff --git a/uikit/src/main/java/com/sendbird/uikit/internal/model/template_messages/ColorIntAsStringSerializer.kt b/uikit/src/main/java/com/sendbird/uikit/internal/model/template_messages/ColorIntAsStringSerializer.kt new file mode 100644 index 00000000..bb999454 --- /dev/null +++ b/uikit/src/main/java/com/sendbird/uikit/internal/model/template_messages/ColorIntAsStringSerializer.kt @@ -0,0 +1,25 @@ +package com.sendbird.uikit.internal.model.template_messages + +import android.graphics.Color +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder + +internal object ColorIntAsStringSerializer : KSerializer { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("ColorInt", PrimitiveKind.STRING) + + override fun deserialize(decoder: Decoder): Int { + val decoded = decoder.decodeString() + // Logger.i("deserialize hex=$decoded") + return Color.parseColor(decoded) + } + + override fun serialize(encoder: Encoder, value: Int) { + val hex = String.format("#%08X", 0xFFFFFFFF and value.toLong()) + // Logger.i("serialize hex=$hex") + encoder.encodeString(hex) + } +} diff --git a/uikit/src/main/java/com/sendbird/uikit/internal/model/template_messages/Enums.kt b/uikit/src/main/java/com/sendbird/uikit/internal/model/template_messages/Enums.kt new file mode 100644 index 00000000..1f8458b3 --- /dev/null +++ b/uikit/src/main/java/com/sendbird/uikit/internal/model/template_messages/Enums.kt @@ -0,0 +1,117 @@ +package com.sendbird.uikit.internal.model.template_messages + +import android.graphics.Typeface +import android.view.Gravity +import android.widget.ImageView +import android.widget.LinearLayout +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +internal enum class ViewType { + @SerialName(KeySet.box) + Box, + + @SerialName(KeySet.image) + Image, + + @SerialName(KeySet.textButton) + Button, + + @SerialName(KeySet.imageButton) + ImageButton, + + @SerialName(KeySet.text) + Text + ; + + companion object { + @JvmStatic + fun from(value: String): ViewType { + return values().first { it.name == value } + } + } +} + +@Serializable +internal enum class Orientation(val value: Int) { + @SerialName(KeySet.row) + Row(LinearLayout.HORIZONTAL), + + @SerialName(KeySet.column) + Column(LinearLayout.VERTICAL) +} + +@Serializable +internal enum class Weight(val value: Int) { + @SerialName(KeySet.normal) + Normal(Typeface.NORMAL), + + @SerialName(KeySet.bold) + Bold(Typeface.BOLD) +} + +@Serializable +internal enum class ContentMode(val scaleType: ImageView.ScaleType) { + @SerialName(KeySet.aspectFill) + CenterCrop(ImageView.ScaleType.CENTER_CROP), + + @SerialName(KeySet.aspectFit) + FitCenter(ImageView.ScaleType.FIT_CENTER), + + @SerialName(KeySet.scalesToFill) + FitXY(ImageView.ScaleType.FIT_XY); + + fun toValueAsSerialName(): String { + return when (this) { + CenterCrop -> KeySet.aspectFill + FitCenter -> KeySet.aspectFit + FitXY -> KeySet.scalesToFill + } + } +} + +@Serializable +internal enum class ActionType { + @SerialName(KeySet.web) + Web, + + @SerialName(KeySet.custom) + Custom, + + @SerialName(KeySet.uikit) + Uikit +} + +@Serializable +internal enum class SizeType { + @SerialName(KeySet.fixed) + Fixed, + + @SerialName(KeySet.flex) + Flex +} + +@Serializable +internal enum class VerticalAlign(val value: Int) { + @SerialName(KeySet.top) + Top(Gravity.TOP), + + @SerialName(KeySet.bottom) + Bottom(Gravity.BOTTOM), + + @SerialName(KeySet.center) + Center(Gravity.CENTER_VERTICAL) +} + +@Serializable +internal enum class HorizontalAlign(val value: Int) { + @SerialName(KeySet.left) + Left(Gravity.START), + + @SerialName(KeySet.right) + Right(Gravity.END), + + @SerialName(KeySet.center) + Center(Gravity.CENTER_HORIZONTAL) +} diff --git a/uikit/src/main/java/com/sendbird/uikit/internal/model/template_messages/KeySet.kt b/uikit/src/main/java/com/sendbird/uikit/internal/model/template_messages/KeySet.kt new file mode 100644 index 00000000..d89335a0 --- /dev/null +++ b/uikit/src/main/java/com/sendbird/uikit/internal/model/template_messages/KeySet.kt @@ -0,0 +1,59 @@ +package com.sendbird.uikit.internal.model.template_messages + +internal object KeySet { + const val version = "version" + const val type = "type" + const val layout = "layout" + const val width = "width" + const val height = "height" + const val value = "value" + const val url = "url" + const val data = "data" + const val alterData = "alterData" + const val align = "align" + const val backgroundColor = "backgroundColor" + const val backgroundImageUrl = "backgroundImageUrl" + const val borderWidth = "borderWidth" + const val borderColor = "borderColor" + const val radius = "radius" + const val items = "items" + const val imageButton = "imageButton" + const val box = "box" + const val image = "image" + const val textButton = "textButton" + const val text = "text" + const val maxTextLines = "maxTextLines" + const val size = "size" + const val color = "color" + const val imageUrl = "imageUrl" + const val weight = "weight" + const val contentMode = "contentMode" + const val viewStyle = "viewStyle" + const val textStyle = "textStyle" + const val buttonStyle = "buttonStyle" + const val imageStyle = "imageStyle" + const val row = "row" + const val column = "column" + const val normal = "normal" + const val bold = "bold" + const val action = "action" + const val aspectFill = "aspectFill" + const val contain = "contain" + const val aspectFit = "aspectFit" + const val scalesToFill = "scalesToFill" + const val web = "web" + const val custom = "custom" + const val uikit = "uikit" + const val fixed = "fixed" + const val flex = "flex" + const val padding = "padding" + const val margin = "margin" + const val top = "top" + const val bottom = "bottom" + const val center = "center" + const val left = "left" + const val right = "right" + const val horizontal = "horizontal" + const val vertical = "vertical" + const val sub_data = "sub_data" +} diff --git a/uikit/src/main/java/com/sendbird/uikit/internal/model/template_messages/Params.kt b/uikit/src/main/java/com/sendbird/uikit/internal/model/template_messages/Params.kt new file mode 100644 index 00000000..fa12b4a3 --- /dev/null +++ b/uikit/src/main/java/com/sendbird/uikit/internal/model/template_messages/Params.kt @@ -0,0 +1,221 @@ +package com.sendbird.uikit.internal.model.template_messages + +import android.content.Context +import android.graphics.Color +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import com.sendbird.android.message.BaseMessage +import com.sendbird.uikit.SendbirdUIKit +import com.sendbird.uikit.interfaces.OnMessageTemplateActionHandler +import com.sendbird.uikit.internal.extensions.intToDp +import com.sendbird.uikit.model.Action +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonClassDiscriminator +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.put + +const val FILL_PARENT = 0 +const val WRAP_CONTENT = 1 + +@Serializable +internal data class ActionData constructor( + val type: ActionType = ActionType.Web, + val data: String, + val alterData: String? = null +) { + fun register(view: View, onMessageTemplateActionHandler: OnMessageTemplateActionHandler?, message: BaseMessage) { + view.setOnClickListener { + onMessageTemplateActionHandler?.onHandleAction(it, Action.from(this@ActionData), message) + } + } + + companion object { + fun create( + type: String = KeySet.web, + data: String, + customData: String? = null + ): JsonObject { + return buildJsonObject { + put(KeySet.type, type) + put(KeySet.data, data) + customData?.let { put(KeySet.alterData, data) } + } + } + } +} + +@Serializable +internal data class SizeSpec constructor( + val type: SizeType, + @SerialName(KeySet.value) + private val _value: Int +) { + internal val value: Int + get() = when (type) { + SizeType.Fixed -> _value + SizeType.Flex -> { + when (_value) { + FILL_PARENT -> ViewGroup.LayoutParams.MATCH_PARENT + WRAP_CONTENT -> ViewGroup.LayoutParams.WRAP_CONTENT + else -> _value + } + } + } + + fun getWeight(): Float { + return when (type) { + SizeType.Fixed -> 0F + SizeType.Flex -> { + when (_value) { + FILL_PARENT -> 1F + WRAP_CONTENT -> 0F + else -> 0F + } + } + } + } +} + +@Serializable +internal data class Align constructor( + private val horizontal: HorizontalAlign = HorizontalAlign.Left, + private val vertical: VerticalAlign = VerticalAlign.Top +) { + val gravity: Int + get() = horizontal.value or vertical.value + + companion object { + fun create( + horizontal: String = KeySet.left, + vertical: String = KeySet.top + ): JsonObject { + return buildJsonObject { + put(KeySet.horizontal, horizontal) + put(KeySet.vertical, vertical) + } + } + } +} + +@Serializable +internal data class MetaData constructor( + val pixelWidth: Int, + val pixelHeight: Int +) + +@Serializable +internal data class Params( + val version: Int, + val body: Body +) + +@Serializable +internal data class Body( + val items: List +) + +@OptIn(ExperimentalSerializationApi::class) +@Serializable +@JsonClassDiscriminator(KeySet.type) +internal sealed class ViewParams { + abstract val type: ViewType + abstract val action: ActionData? + abstract val width: SizeSpec + abstract val height: SizeSpec + abstract val viewStyle: ViewStyle + + private fun getWeight(orientation: Orientation): Float { + return when (orientation) { + Orientation.Row -> { + width.getWeight() + } + Orientation.Column -> { + height.getWeight() + } + } + } + + fun createLayoutParams(context: Context, orientation: Orientation): LinearLayout.LayoutParams { + val weight = getWeight(orientation) + val resources = context.resources + val w = if (width.type == SizeType.Fixed) resources.intToDp(width.value) else width.value + val h = if (height.type == SizeType.Fixed) resources.intToDp(height.value) else height.value + return LinearLayout.LayoutParams(w, h, weight) + } +} + +@Serializable +@SerialName(KeySet.box) +internal data class BoxViewParams constructor( + override val type: ViewType, + override val action: ActionData? = null, + override val width: SizeSpec = SizeSpec(SizeType.Flex, FILL_PARENT), + override val height: SizeSpec = SizeSpec(SizeType.Flex, WRAP_CONTENT), + override val viewStyle: ViewStyle = ViewStyle(), + val align: Align = Align(), + @SerialName(KeySet.layout) + val orientation: Orientation = Orientation.Row, + val items: List? = null +) : ViewParams() + +@Serializable +@SerialName(KeySet.text) +internal data class TextViewParams constructor( + override val type: ViewType, + override val action: ActionData? = null, + override val width: SizeSpec = SizeSpec(SizeType.Flex, FILL_PARENT), + override val height: SizeSpec = SizeSpec(SizeType.Flex, WRAP_CONTENT), + override val viewStyle: ViewStyle = ViewStyle(), + val align: Align = Align(), + val text: String, + val maxTextLines: Int? = null, + val textStyle: TextStyle = TextStyle() +) : ViewParams() + +@Serializable +@SerialName(KeySet.image) +internal data class ImageViewParams constructor( + override val type: ViewType, + override val action: ActionData? = null, + override val width: SizeSpec = SizeSpec(SizeType.Flex, FILL_PARENT), + override val height: SizeSpec = SizeSpec(SizeType.Flex, WRAP_CONTENT), + override val viewStyle: ViewStyle = ViewStyle(), + val imageUrl: String, + val metaData: MetaData? = null, + val imageStyle: ImageStyle = ImageStyle() +) : ViewParams() + +@Serializable +@SerialName(KeySet.textButton) +internal data class ButtonViewParams constructor( + override val type: ViewType, + override val action: ActionData? = null, + override val width: SizeSpec = SizeSpec(SizeType.Flex, FILL_PARENT), + override val height: SizeSpec = SizeSpec(SizeType.Flex, WRAP_CONTENT), + override val viewStyle: ViewStyle = ViewStyle(), + val text: String, + val maxTextLines: Int = 1, + val textStyle: TextStyle = TextStyle( + color = when (SendbirdUIKit.getDefaultThemeMode()) { + SendbirdUIKit.ThemeMode.Light -> Color.parseColor("#742ddd") + SendbirdUIKit.ThemeMode.Dark -> Color.parseColor("#c2a9fa") + }, + weight = Weight.Bold + ) +) : ViewParams() + +@Serializable +@SerialName(KeySet.imageButton) +internal data class ImageButtonViewParams constructor( + override val type: ViewType, + override val action: ActionData? = null, + override val width: SizeSpec = SizeSpec(SizeType.Flex, FILL_PARENT), + override val height: SizeSpec = SizeSpec(SizeType.Flex, WRAP_CONTENT), + override val viewStyle: ViewStyle = ViewStyle(), + val imageUrl: String, + val imageStyle: ImageStyle = ImageStyle() +) : ViewParams() diff --git a/uikit/src/main/java/com/sendbird/uikit/internal/model/template_messages/Styles.kt b/uikit/src/main/java/com/sendbird/uikit/internal/model/template_messages/Styles.kt new file mode 100644 index 00000000..76844abc --- /dev/null +++ b/uikit/src/main/java/com/sendbird/uikit/internal/model/template_messages/Styles.kt @@ -0,0 +1,113 @@ +package com.sendbird.uikit.internal.model.template_messages + +import android.graphics.Color +import android.graphics.drawable.GradientDrawable +import android.util.TypedValue +import android.view.View +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import androidx.annotation.ColorInt +import com.sendbird.uikit.internal.RoundableView +import com.sendbird.uikit.internal.extensions.intToDp +import com.sendbird.uikit.internal.extensions.loadToBackground +import kotlinx.serialization.Serializable + +@Serializable +internal data class TextStyle( + val size: Int? = null, + @ColorInt + @Serializable(with = ColorIntAsStringSerializer::class) + val color: Int? = null, + val weight: Weight? = null +) { + fun apply(view: TextView): TextStyle { + size?.let { view.setTextSize(TypedValue.COMPLEX_UNIT_SP, it.toFloat()) } + color?.let { view.setTextColor(it) } + weight?.let { view.setTypeface(view.typeface, it.value) } + return this + } +} + +@Serializable +internal data class ImageStyle( + val contentMode: ContentMode? = null +) { + fun apply(view: ImageView): ImageStyle { + contentMode?.let { view.scaleType = it.scaleType } + return this + } +} + +@Serializable +internal data class ViewStyle( + @ColorInt + @Serializable(with = ColorIntAsStringSerializer::class) + val backgroundColor: Int? = null, + val backgroundImageUrl: String? = null, + val borderWidth: Int? = null, + @ColorInt + @Serializable(with = ColorIntAsStringSerializer::class) + val borderColor: Int? = null, + val radius: Int? = null, + val margin: Margin? = null, + val padding: Padding? = null +) { + fun apply(view: View, useRipple: Boolean = false): ViewStyle { + val resources = view.context.resources + backgroundImageUrl?.let { view.loadToBackground(it, radius ?: 0, useRipple) } + if (backgroundColor != null || (borderWidth != null && borderWidth > 0)) { + view.background = GradientDrawable().apply { + backgroundColor?.let { setColor(it) } + cornerRadius = resources.intToDp(radius ?: 0).toFloat() + } + } + + margin?.apply(view) + padding?.apply(view) + + if (view is RoundableView) { + radius?.let { view.setRadiusIntSize(it) } + borderWidth?.let { view.setBorder(borderWidth, borderColor ?: Color.TRANSPARENT) } + } + return this + } +} + +@Serializable +internal data class Margin constructor( + val top: Int = 0, + val bottom: Int = 0, + val left: Int = 0, + val right: Int = 0 +) { + fun apply(view: View) { + val resources = view.context.resources + val layoutParams = view.layoutParams as LinearLayout.LayoutParams + layoutParams.setMargins( + resources.intToDp(left), + resources.intToDp(top), + resources.intToDp(right), + resources.intToDp(bottom) + ) + view.layoutParams = layoutParams + } +} + +@Serializable +internal data class Padding constructor( + val top: Int = 0, + val bottom: Int = 0, + val left: Int = 0, + val right: Int = 0 +) { + fun apply(view: View) { + val resources = view.context.resources + view.setPadding( + resources.intToDp(left), + resources.intToDp(top), + resources.intToDp(right), + resources.intToDp(bottom) + ) + } +} diff --git a/uikit/src/main/java/com/sendbird/uikit/internal/model/template_messages/Template.kt b/uikit/src/main/java/com/sendbird/uikit/internal/model/template_messages/Template.kt new file mode 100644 index 00000000..6de2463d --- /dev/null +++ b/uikit/src/main/java/com/sendbird/uikit/internal/model/template_messages/Template.kt @@ -0,0 +1,175 @@ +package com.sendbird.uikit.internal.model.template_messages + +import kotlinx.serialization.json.JsonArray +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.put + +internal object Template { + private fun createImage( + action: JsonObject? = null, + width: JsonObject? = null, + height: JsonObject? = null, + viewStyle: JsonObject? = null, + imageUrl: String, + imageStyle: JsonObject? = null, + ): JsonObject { + return buildJsonObject { + put(KeySet.type, KeySet.image) + action?.let { put(KeySet.action, it) } + width?.let { put(KeySet.width, it) } + height?.let { put(KeySet.height, it) } + viewStyle?.let { put(KeySet.viewStyle, it) } + put(KeySet.imageUrl, imageUrl) + imageStyle?.let { put(KeySet.imageStyle, it) } + } + } + + private fun createButton( + action: JsonObject? = null, + width: JsonObject? = null, + height: JsonObject? = null, + viewStyle: JsonObject? = null, + text: String, + maxTextLines: Int? = null, + textStyle: JsonObject? = null, + ): JsonObject { + return buildJsonObject { + put(KeySet.type, KeySet.textButton) + action?.let { put(KeySet.action, it) } + width?.let { put(KeySet.width, it) } + height?.let { put(KeySet.height, it) } + viewStyle?.let { put(KeySet.viewStyle, it) } + put(KeySet.text, text) + maxTextLines?.let { put(KeySet.maxTextLines, it) } + textStyle?.let { put(KeySet.textStyle, it) } + } + } + + private fun createImageButton( + action: JsonObject? = null, + width: JsonObject? = null, + height: JsonObject? = null, + viewStyle: JsonObject? = null, + imageUrl: String, + imageStyle: JsonObject? = null, + ): JsonObject { + return buildJsonObject { + put(KeySet.type, KeySet.imageButton) + action?.let { put(KeySet.action, it) } + width?.let { put(KeySet.width, it) } + height?.let { put(KeySet.height, it) } + viewStyle?.let { put(KeySet.viewStyle, it) } + put(KeySet.imageUrl, imageUrl) + imageStyle?.let { put(KeySet.imageStyle, it) } + } + } + + private fun createText( + action: JsonObject? = null, + width: JsonObject? = null, + height: JsonObject? = null, + align: JsonObject? = null, + viewStyle: JsonObject? = null, + text: String, + maxTextLines: Int? = null, + textStyle: JsonObject? = null, + ): JsonObject { + return buildJsonObject { + put(KeySet.type, KeySet.text) + action?.let { put(KeySet.action, it) } + width?.let { put(KeySet.width, it) } + height?.let { put(KeySet.height, it) } + align?.let { put(KeySet.align, it) } + viewStyle?.let { put(KeySet.viewStyle, it) } + put(KeySet.text, text) + maxTextLines?.let { put(KeySet.maxTextLines, it) } + textStyle?.let { put(KeySet.textStyle, it) } + } + } + + private fun createBox( + action: JsonObject? = null, + width: JsonObject? = null, + height: JsonObject? = null, + align: JsonObject? = null, + viewStyle: JsonObject? = null, + layout: String? = null, + items: JsonArray + ): JsonObject { + return buildJsonObject { + put(KeySet.type, KeySet.box) + action?.let { put(KeySet.action, it) } + width?.let { put(KeySet.width, it) } + height?.let { put(KeySet.height, it) } + align?.let { put(KeySet.align, it) } + viewStyle?.let { put(KeySet.viewStyle, it) } + layout?.let { put(KeySet.layout, it) } + put(KeySet.items, items) + } + } + + private fun createSize( + type: String, + value: Int + ): JsonObject { + return buildJsonObject { + put(KeySet.type, type) + put(KeySet.value, value) + } + } + + private fun createRect( + top: Int? = null, + bottom: Int? = null, + left: Int? = null, + right: Int? = null, + ): JsonObject { + return buildJsonObject { + top?.let { put(KeySet.top, it) } + bottom?.let { put(KeySet.bottom, it) } + left?.let { put(KeySet.left, it) } + right?.let { put(KeySet.right, it) } + } + } + + private fun createTextStyle( + size: Int? = null, + color: String? = null, + weight: String? = null, + ): JsonObject { + return buildJsonObject { + size?.let { put(KeySet.size, it) } + color?.let { put(KeySet.color, it) } + weight?.let { put(KeySet.weight, it) } + } + } + + private fun createImageStyle( + contentMode: String? = null, + ): JsonObject { + return buildJsonObject { + contentMode?.let { put(KeySet.contentMode, it) } + } + } + + private fun createViewStyle( + backgroundColor: String? = null, + backgroundImageUrl: String? = null, + borderWidth: Int? = null, + borderColor: String? = null, + radius: Int? = null, + margin: JsonObject? = null, + padding: JsonObject? = null + ): JsonObject { + return buildJsonObject { + backgroundColor?.let { put(KeySet.backgroundColor, it) } + backgroundImageUrl?.let { put(KeySet.backgroundImageUrl, it) } + borderWidth?.let { put(KeySet.borderWidth, it) } + borderColor?.let { put(KeySet.borderColor, it) } + radius?.let { put(KeySet.radius, it) } + margin?.let { put(KeySet.margin, it) } + padding?.let { put(KeySet.padding, it) } + } + } +} diff --git a/uikit/src/main/java/com/sendbird/uikit/internal/model/template_messages/TemplateViewGenerator.kt b/uikit/src/main/java/com/sendbird/uikit/internal/model/template_messages/TemplateViewGenerator.kt new file mode 100644 index 00000000..ba660b09 --- /dev/null +++ b/uikit/src/main/java/com/sendbird/uikit/internal/model/template_messages/TemplateViewGenerator.kt @@ -0,0 +1,99 @@ +package com.sendbird.uikit.internal.model.template_messages + +import android.content.Context +import android.view.View +import android.view.ViewGroup +import com.sendbird.uikit.internal.ui.widgets.Box +import com.sendbird.uikit.internal.ui.widgets.Image +import com.sendbird.uikit.internal.ui.widgets.ImageButton +import com.sendbird.uikit.internal.ui.widgets.Text +import com.sendbird.uikit.internal.ui.widgets.TextButton + +internal typealias ViewLifecycleHandler = (view: View, viewParams: ViewParams) -> Unit + +internal object TemplateViewGenerator { + fun generateView( + context: Context, + viewParams: ViewParams, + orientation: Orientation, + onViewCreated: ViewLifecycleHandler? = null + ): View { + return when (viewParams) { + is BoxViewParams -> createBoxView(context, viewParams, orientation, onViewCreated) + is ImageViewParams -> createImageView(context, viewParams, orientation, onViewCreated) + is TextViewParams -> createTextView(context, viewParams, orientation, onViewCreated) + is ButtonViewParams -> createButtonView(context, viewParams, orientation, onViewCreated) + is ImageButtonViewParams -> createImageButtonView(context, viewParams, orientation, onViewCreated) + } + } + + private fun createTextView( + context: Context, + params: TextViewParams, + orientation: Orientation, + onViewCreated: ViewLifecycleHandler? = null + ): View { + return Text(context).apply { + onViewCreated?.invoke(this, params) + apply(params, orientation) + } + } + + private fun createImageView( + context: Context, + params: ImageViewParams, + orientation: Orientation, + onViewCreated: ViewLifecycleHandler? = null + ): View { + return Image(context).apply { + onViewCreated?.invoke(this, params) + apply(params, orientation) + } + } + + private fun createButtonView( + context: Context, + params: ButtonViewParams, + orientation: Orientation, + onViewCreated: ViewLifecycleHandler? = null + ): View { + return TextButton(context).apply { + onViewCreated?.invoke(this, params) + apply(params, orientation) + } + } + + private fun createImageButtonView( + context: Context, + params: ImageButtonViewParams, + orientation: Orientation, + onViewCreated: ViewLifecycleHandler? = null + ): View { + return ImageButton(context).apply { + onViewCreated?.invoke(this, params) + apply(params, orientation) + } + } + + private fun createBoxView( + context: Context, + params: BoxViewParams, + orientation: Orientation, + onViewCreated: ViewLifecycleHandler? = null + ): ViewGroup { + return Box(context).apply { + onViewCreated?.invoke(this, params) + apply(params, orientation) + params.items?.forEach { + addView( + generateView( + context, + it, + params.orientation, + onViewCreated + ) + ) + } + } + } +} diff --git a/uikit/src/main/java/com/sendbird/uikit/internal/queries/BannedUserListQuery.kt b/uikit/src/main/java/com/sendbird/uikit/internal/queries/BannedUserListQuery.kt index 5861dcb0..681be662 100644 --- a/uikit/src/main/java/com/sendbird/uikit/internal/queries/BannedUserListQuery.kt +++ b/uikit/src/main/java/com/sendbird/uikit/internal/queries/BannedUserListQuery.kt @@ -16,9 +16,11 @@ internal class BannedUserListQuery( ) : PagedQueryHandler { private var query: BannedUserListQuery? = null override fun loadInitial(handler: OnListResultHandler) { - query = createBannedUserListQuery(BannedUserListQueryParams(channelType, channelUrl).apply { - limit = 30 - }) + query = createBannedUserListQuery( + BannedUserListQueryParams(channelType, channelUrl).apply { + limit = 30 + } + ) loadMore(handler) } diff --git a/uikit/src/main/java/com/sendbird/uikit/internal/queries/MutedMemberListQuery.kt b/uikit/src/main/java/com/sendbird/uikit/internal/queries/MutedMemberListQuery.kt index 2962bc50..5e347e1b 100644 --- a/uikit/src/main/java/com/sendbird/uikit/internal/queries/MutedMemberListQuery.kt +++ b/uikit/src/main/java/com/sendbird/uikit/internal/queries/MutedMemberListQuery.kt @@ -11,10 +11,13 @@ import com.sendbird.uikit.interfaces.PagedQueryHandler internal class MutedMemberListQuery(private val channelUrl: String) : PagedQueryHandler { private var query: MemberListQuery? = null override fun loadInitial(handler: OnListResultHandler) { - query = GroupChannel.createMemberListQuery(channelUrl, MemberListQueryParams().apply { - limit = 30 - mutedMemberFilter = MutedMemberFilter.MUTED - }) + query = GroupChannel.createMemberListQuery( + channelUrl, + MemberListQueryParams().apply { + limit = 30 + mutedMemberFilter = MutedMemberFilter.MUTED + } + ) loadMore(handler) } diff --git a/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/MessageTemplateView.kt b/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/MessageTemplateView.kt new file mode 100644 index 00000000..521e8b78 --- /dev/null +++ b/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/MessageTemplateView.kt @@ -0,0 +1,111 @@ +package com.sendbird.uikit.internal.ui.messages + +import android.content.Context +import android.util.AttributeSet +import android.view.View +import android.widget.LinearLayout +import com.sendbird.android.message.BaseMessage +import com.sendbird.uikit.R +import com.sendbird.uikit.interfaces.OnMessageTemplateActionHandler +import com.sendbird.uikit.internal.model.template_messages.ContentMode +import com.sendbird.uikit.internal.model.template_messages.Orientation +import com.sendbird.uikit.internal.model.template_messages.TemplateViewGenerator +import com.sendbird.uikit.internal.model.template_messages.ViewParams +import com.sendbird.uikit.internal.ui.widgets.Box +import com.sendbird.uikit.internal.ui.widgets.Image +import com.sendbird.uikit.internal.ui.widgets.ImageButton +import com.sendbird.uikit.internal.ui.widgets.Text +import com.sendbird.uikit.internal.ui.widgets.TextButton + +internal class MessageTemplateView @JvmOverloads internal constructor( + context: Context, + attrs: AttributeSet? = null, + defStyle: Int = R.attr.sb_widget_message_template +) : LinearLayout(context, attrs, defStyle) { + + private val defaultMessageTemplateStyle: DefaultMessageTemplateStyle + + init { + this.orientation = VERTICAL + layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT) + + val a = context.theme.obtainStyledAttributes(attrs, R.styleable.MessageView_MessageTemplate, defStyle, 0) + try { + val textAppearanceInTextView = a.getResourceId( + R.styleable.MessageView_MessageTemplate_sb_message_textview_text_appearance, + R.style.SendbirdBody3OnLight01 + ) + val buttonBackground = a.getResourceId( + R.styleable.MessageView_MessageTemplate_sb_message_button_background, + R.color.onlight_04 + ) + val buttonTextAppearance = a.getResourceId( + R.styleable.MessageView_MessageTemplate_sb_message_button_text_appearance, + R.style.SendbirdButtonPrimary300 + ) + val buttonRadius = a.getDimensionPixelSize( + R.styleable.MessageView_MessageTemplate_sb_message_button_radius, + context.resources.getDimensionPixelSize(R.dimen.sb_size_6) + ) + + defaultMessageTemplateStyle = DefaultMessageTemplateStyle( + textAppearanceInTextView, + buttonBackground, + buttonTextAppearance, + buttonRadius + ) + } finally { + a.recycle() + } + } + + internal fun inflateViews( + params: List, + message: BaseMessage, + onMessageTemplateActionHandler: OnMessageTemplateActionHandler? = null + ) { + params.forEach { + addView(createView(it, message, onMessageTemplateActionHandler)) + } + } + + private fun createView( + viewParams: ViewParams, + message: BaseMessage, + onMessageTemplateActionHandler: OnMessageTemplateActionHandler? = null + ): View { + return TemplateViewGenerator.generateView( + context, + viewParams, + Orientation.Column, + onViewCreated = { view, params -> + params.action?.register(view, onMessageTemplateActionHandler, message) + when (view) { + is Text -> defaultMessageTemplateStyle.apply(view) + is TextButton -> defaultMessageTemplateStyle.apply(view) + is Image -> view.scaleType = ContentMode.FitCenter.scaleType + is ImageButton -> {} + is Box -> {} + } + }, + ) + } +} + +internal class DefaultMessageTemplateStyle( + private val textAppearanceInTextView: Int, + private val buttonBackground: Int, + private val buttonTextAppearance: Int, + private val buttonRadius: Int +) { + + fun apply(textView: Text) { + textView.setTextAppearance(textAppearanceInTextView) + } + + fun apply(textButton: TextButton) { + textButton.setBackgroundResource(buttonBackground) + textButton.setTextAppearance(buttonTextAppearance) + textButton.radius = buttonRadius.toFloat() + } +} diff --git a/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/NotificationChannelMessageView.kt b/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/NotificationChannelMessageView.kt new file mode 100644 index 00000000..0e30011b --- /dev/null +++ b/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/NotificationChannelMessageView.kt @@ -0,0 +1,88 @@ +package com.sendbird.uikit.internal.ui.messages + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import com.sendbird.android.message.BaseMessage +import com.sendbird.uikit.R +import com.sendbird.uikit.databinding.SbViewMessageNotificationChannelComponentBinding +import com.sendbird.uikit.interfaces.OnMessageTemplateActionHandler +import com.sendbird.uikit.internal.MessageTemplateParser +import com.sendbird.uikit.internal.extensions.setAppearance +import com.sendbird.uikit.utils.DateUtils +import com.sendbird.uikit.utils.DrawableUtils +import com.sendbird.uikit.utils.ViewUtils + +internal class NotificationChannelMessageView @JvmOverloads internal constructor( + context: Context, + attrs: AttributeSet? = null, + defStyle: Int = R.attr.sb_widget_notification_channel_message +) : BaseMessageView(context, attrs, defStyle) { + override val binding: SbViewMessageNotificationChannelComponentBinding + override val layout: View + get() = binding.root + + var onMessageTemplateActionHandler: OnMessageTemplateActionHandler? = null + + init { + val a = + context.theme.obtainStyledAttributes(attrs, R.styleable.MessageView_NotificationChannel, defStyle, 0) + try { + binding = SbViewMessageNotificationChannelComponentBinding.inflate( + LayoutInflater.from(context), + this, + true + ) + val messageBackground = a.getResourceId( + R.styleable.MessageView_NotificationChannel_sb_notification_channel_message_background, + R.color.background_100 + ) + val leftCaptionAppearance = a.getResourceId( + R.styleable.MessageView_NotificationChannel_sb_message_sender_name_text_appearance, + R.style.SendbirdCaption1OnLight02 + ) + val rightCaptionAppearance = a.getResourceId( + R.styleable.MessageView_NotificationChannel_sb_message_time_text_appearance, + R.style.SendbirdCaption4OnLight03 + ) + val bubbleRadius = a.getDimensionPixelSize( + R.styleable.MessageView_NotificationChannel_sb_notification_channel_message_radius, + context.resources.getDimensionPixelSize(R.dimen.sb_size_8) + ) + val newBadgeColor = a.getResourceId( + R.styleable.MessageView_NotificationChannel_sb_notification_channel_new_badge_color, + R.color.secondary_300 + ) + + binding.contentPanel.setBackgroundResource(messageBackground) + binding.tvLeftCaption.setAppearance(context, leftCaptionAppearance) + binding.tvRightCaption.setAppearance(context, rightCaptionAppearance) + binding.contentPanel.radius = bubbleRadius.toFloat() + binding.ivNewBadge.background = DrawableUtils.createOvalIcon(context, newBadgeColor) + } finally { + a.recycle() + } + } + + fun drawMessage(message: BaseMessage, shouldDisplayUserProfile: Boolean, lastSeen: Long) { + if (shouldDisplayUserProfile) { + ViewUtils.drawNotificationProfile(binding.ivProfileView, message) + } + binding.ivProfileView.visibility = if (shouldDisplayUserProfile) VISIBLE else GONE + + binding.tvLeftCaption.text = message.customType + binding.tvRightCaption.text = DateUtils.formatDateTime(context, message.createdAt) + binding.ivNewBadge.visibility = + if (message.createdAt > lastSeen) View.VISIBLE else View.GONE + + binding.contentPanel.removeAllViews() + val params = MessageTemplateParser.parseParams( + message, + context.getString(R.string.sb_text_notification_channel_error) + ) + val layout = MessageTemplateView(context) + layout.inflateViews(params.body.items, message, onMessageTemplateActionHandler) + binding.contentPanel.addView(layout) + } +} diff --git a/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/OtherFileMessageView.kt b/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/OtherFileMessageView.kt index 80fef375..26056273 100644 --- a/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/OtherFileMessageView.kt +++ b/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/OtherFileMessageView.kt @@ -37,7 +37,7 @@ internal class OtherFileMessageView @JvmOverloads constructor( messageGroupType == MessageGroupType.GROUPING_TYPE_SINGLE || messageGroupType == MessageGroupType.GROUPING_TYPE_TAIL val showNickname = (messageGroupType == MessageGroupType.GROUPING_TYPE_SINGLE || messageGroupType == MessageGroupType.GROUPING_TYPE_HEAD) && - (!params.shouldUseQuotedView() || !MessageUtils.hasParentMessage(message)) + (!params.shouldUseQuotedView() || !MessageUtils.hasParentMessage(message)) binding.ivProfileView.visibility = if (showProfile) VISIBLE else INVISIBLE binding.tvNickname.visibility = if (showNickname) VISIBLE else GONE diff --git a/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/OtherImageFileMessageView.kt b/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/OtherImageFileMessageView.kt index c9a66e50..68e21be9 100644 --- a/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/OtherImageFileMessageView.kt +++ b/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/OtherImageFileMessageView.kt @@ -36,7 +36,7 @@ internal class OtherImageFileMessageView @JvmOverloads constructor( messageGroupType == MessageGroupType.GROUPING_TYPE_SINGLE || messageGroupType == MessageGroupType.GROUPING_TYPE_TAIL val showNickname = (messageGroupType == MessageGroupType.GROUPING_TYPE_SINGLE || messageGroupType == MessageGroupType.GROUPING_TYPE_HEAD) && - (!params.shouldUseQuotedView() || !MessageUtils.hasParentMessage(message)) + (!params.shouldUseQuotedView() || !MessageUtils.hasParentMessage(message)) binding.ivProfileView.visibility = if (showProfile) VISIBLE else INVISIBLE binding.tvNickname.visibility = if (showNickname) VISIBLE else GONE diff --git a/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/OtherUserMessageView.kt b/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/OtherUserMessageView.kt index 3260390e..2f914320 100644 --- a/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/OtherUserMessageView.kt +++ b/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/OtherUserMessageView.kt @@ -122,7 +122,7 @@ internal class OtherUserMessageView @JvmOverloads internal constructor( messageGroupType == MessageGroupType.GROUPING_TYPE_SINGLE || messageGroupType == MessageGroupType.GROUPING_TYPE_TAIL val showNickname = (messageGroupType == MessageGroupType.GROUPING_TYPE_SINGLE || messageGroupType == MessageGroupType.GROUPING_TYPE_HEAD) && - (!params.shouldUseQuotedView() || !MessageUtils.hasParentMessage(message)) + (!params.shouldUseQuotedView() || !MessageUtils.hasParentMessage(message)) binding.ivProfileView.visibility = if (showProfile) VISIBLE else INVISIBLE binding.tvNickname.visibility = if (showNickname) VISIBLE else GONE diff --git a/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/OtherVideoFileMessageView.kt b/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/OtherVideoFileMessageView.kt index 05f1525c..bed6d61d 100644 --- a/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/OtherVideoFileMessageView.kt +++ b/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/OtherVideoFileMessageView.kt @@ -36,7 +36,7 @@ internal class OtherVideoFileMessageView @JvmOverloads constructor( messageGroupType == MessageGroupType.GROUPING_TYPE_SINGLE || messageGroupType == MessageGroupType.GROUPING_TYPE_TAIL val showNickname = (messageGroupType == MessageGroupType.GROUPING_TYPE_SINGLE || messageGroupType == MessageGroupType.GROUPING_TYPE_HEAD) && - (!params.shouldUseQuotedView() || !MessageUtils.hasParentMessage(message)) + (!params.shouldUseQuotedView() || !MessageUtils.hasParentMessage(message)) binding.ivProfileView.visibility = if (showProfile) VISIBLE else INVISIBLE binding.tvNickname.visibility = if (showNickname) VISIBLE else GONE diff --git a/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/ParentMessageInfoView.kt b/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/ParentMessageInfoView.kt index 391f85ba..311b413c 100644 --- a/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/ParentMessageInfoView.kt +++ b/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/ParentMessageInfoView.kt @@ -16,7 +16,6 @@ import com.sendbird.uikit.databinding.SbViewParentMessageInfoBinding import com.sendbird.uikit.internal.extensions.setAppearance import com.sendbird.uikit.model.MessageListUIParams import com.sendbird.uikit.model.MessageUIConfig -import com.sendbird.uikit.utils.Available import com.sendbird.uikit.utils.DrawableUtils import com.sendbird.uikit.utils.ReactionUtils import com.sendbird.uikit.utils.ViewUtils diff --git a/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/ThreadInfoView.kt b/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/ThreadInfoView.kt index 75b5da53..7088d96c 100644 --- a/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/ThreadInfoView.kt +++ b/uikit/src/main/java/com/sendbird/uikit/internal/ui/messages/ThreadInfoView.kt @@ -29,7 +29,6 @@ import java.nio.charset.Charset import java.security.MessageDigest import kotlin.math.min - internal class ThreadInfoView @JvmOverloads internal constructor( context: Context, attrs: AttributeSet? = null, @@ -153,7 +152,6 @@ internal class ThreadInfoView @JvmOverloads internal constructor( return DrawableUtils.toBitmap(layerDrawable) ?: toTransform } - override fun updateDiskCacheKey(messageDigest: MessageDigest) { messageDigest.update(ID_BYTES) diff --git a/uikit/src/main/java/com/sendbird/uikit/internal/ui/viewholders/NotificationChannelMessageViewHolder.kt b/uikit/src/main/java/com/sendbird/uikit/internal/ui/viewholders/NotificationChannelMessageViewHolder.kt new file mode 100644 index 00000000..e620b22b --- /dev/null +++ b/uikit/src/main/java/com/sendbird/uikit/internal/ui/viewholders/NotificationChannelMessageViewHolder.kt @@ -0,0 +1,25 @@ +package com.sendbird.uikit.internal.ui.viewholders + +import androidx.recyclerview.widget.RecyclerView +import com.sendbird.android.channel.BaseChannel +import com.sendbird.android.channel.GroupChannel +import com.sendbird.android.message.BaseMessage +import com.sendbird.uikit.databinding.SbViewMessageNotificationChannelBinding +import com.sendbird.uikit.interfaces.OnMessageTemplateActionHandler + +internal class NotificationChannelMessageViewHolder internal constructor( + val binding: SbViewMessageNotificationChannelBinding, + private val shouldDisplayUserProfile: Boolean +) : RecyclerView.ViewHolder(binding.root) { + + fun bind(channel: BaseChannel, message: BaseMessage, lastSeenAt: Long) { + if (channel is GroupChannel) { + binding.messageTemplateView.drawMessage(message, shouldDisplayUserProfile, lastSeenAt) + itemView.requestLayout() + } + } + + fun setOnMessageTemplateActionHandler(handler: OnMessageTemplateActionHandler?) { + binding.messageTemplateView.onMessageTemplateActionHandler = handler + } +} diff --git a/uikit/src/main/java/com/sendbird/uikit/internal/ui/widgets/MessageInputDialogWrapper.kt b/uikit/src/main/java/com/sendbird/uikit/internal/ui/widgets/MessageInputDialogWrapper.kt index 7085de07..618a02cf 100644 --- a/uikit/src/main/java/com/sendbird/uikit/internal/ui/widgets/MessageInputDialogWrapper.kt +++ b/uikit/src/main/java/com/sendbird/uikit/internal/ui/widgets/MessageInputDialogWrapper.kt @@ -16,7 +16,6 @@ import com.sendbird.uikit.log.Logger import com.sendbird.uikit.utils.SoftInputUtils import com.sendbird.uikit.widgets.MessageInputView - internal class MessageInputDialogWrapper @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, @@ -105,7 +104,8 @@ internal class MessageInputDialogWrapper @JvmOverloads constructor( private fun attachInputViewToDialog(messageInputView: MessageInputView) { dialogCustomView.addView( - messageInputView, ViewGroup.LayoutParams( + messageInputView, + ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT ) @@ -115,7 +115,8 @@ internal class MessageInputDialogWrapper @JvmOverloads constructor( private fun attachInputViewToDisplayView(messageInputView: MessageInputView) { binding.contentView.addView( - messageInputView, ViewGroup.LayoutParams( + messageInputView, + ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT ) diff --git a/uikit/src/main/java/com/sendbird/uikit/internal/ui/widgets/MessageTemplateImageView.kt b/uikit/src/main/java/com/sendbird/uikit/internal/ui/widgets/MessageTemplateImageView.kt new file mode 100644 index 00000000..30c1a565 --- /dev/null +++ b/uikit/src/main/java/com/sendbird/uikit/internal/ui/widgets/MessageTemplateImageView.kt @@ -0,0 +1,81 @@ +package com.sendbird.uikit.internal.ui.widgets + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Path +import android.graphics.RectF +import android.util.AttributeSet +import androidx.annotation.ColorInt +import androidx.appcompat.widget.AppCompatImageView +import com.sendbird.uikit.internal.RoundableView +import com.sendbird.uikit.internal.extensions.intToDp + +internal open class MessageTemplateImageView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : AppCompatImageView(context, attrs, defStyleAttr), RoundableView { + private var targetWidth: Int = 0 + private var targetHeight: Int = 0 + private lateinit var rectF: RectF + private val path: Path = Path() + private var strokePaint: Paint? = null + override var radius: Float = 0F + + fun setSize(width: Int, height: Int) { + targetWidth = width + targetHeight = height + requestLayout() + } + + init { + setBorder(0, Color.TRANSPARENT) + } + + override fun setRadiusIntSize(radius: Int) { + this.radius = context.resources.intToDp(radius).toFloat() + } + + final override fun setBorder(borderWidth: Int, @ColorInt borderColor: Int) { + if (borderWidth <= 0) + strokePaint = null + else { + strokePaint = Paint().apply { + style = Paint.Style.STROKE + isAntiAlias = true + strokeWidth = context.resources.intToDp(borderWidth).toFloat() + color = borderColor + } + } + } + + override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { + super.onSizeChanged(w, h, oldw, oldh) + rectF = RectF(0f, 0f, w.toFloat(), h.toFloat()) + resetPath() + } + + override fun draw(canvas: Canvas) { + val save = canvas.save() + canvas.clipPath(path) + super.draw(canvas) + strokePaint?.let { canvas.drawRoundRect(rectF, radius, radius, it) } + canvas.restoreToCount(save) + } + + override fun dispatchDraw(canvas: Canvas) { + val save = canvas.save() + canvas.clipPath(path) + super.dispatchDraw(canvas) + strokePaint?.let { canvas.drawRoundRect(rectF, radius, radius, it) } + canvas.restoreToCount(save) + } + + private fun resetPath() { + path.reset() + path.addRoundRect(rectF, radius, radius, Path.Direction.CW) + path.close() + } +} diff --git a/uikit/src/main/java/com/sendbird/uikit/internal/ui/widgets/RoundCornerLayout.kt b/uikit/src/main/java/com/sendbird/uikit/internal/ui/widgets/RoundCornerLayout.kt new file mode 100755 index 00000000..1a7c67a5 --- /dev/null +++ b/uikit/src/main/java/com/sendbird/uikit/internal/ui/widgets/RoundCornerLayout.kt @@ -0,0 +1,73 @@ +package com.sendbird.uikit.internal.ui.widgets + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Path +import android.graphics.RectF +import android.util.AttributeSet +import android.widget.LinearLayout +import androidx.annotation.ColorInt +import com.sendbird.uikit.internal.RoundableView +import com.sendbird.uikit.internal.extensions.intToDp + +internal open class RoundCornerLayout @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : LinearLayout(context, attrs, defStyleAttr), RoundableView { + private lateinit var rectF: RectF + private val path: Path = Path() + private var strokePaint: Paint? = null + override var radius: Float = 0F + + init { + setBorder(0, Color.TRANSPARENT) + } + + override fun setRadiusIntSize(radius: Int) { + this.radius = context.resources.intToDp(radius).toFloat() + } + + final override fun setBorder(borderWidth: Int, @ColorInt borderColor: Int) { + if (borderWidth <= 0) + strokePaint = null + else { + strokePaint = Paint().apply { + style = Paint.Style.STROKE + isAntiAlias = true + strokeWidth = context.resources.intToDp(borderWidth).toFloat() + color = borderColor + } + } + } + + override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { + super.onSizeChanged(w, h, oldw, oldh) + rectF = RectF(0f, 0f, w.toFloat(), h.toFloat()) + resetPath() + } + + override fun draw(canvas: Canvas) { + val save = canvas.save() + canvas.clipPath(path) + super.draw(canvas) + strokePaint?.let { canvas.drawRoundRect(rectF, radius, radius, it) } + canvas.restoreToCount(save) + } + + override fun dispatchDraw(canvas: Canvas) { + val save = canvas.save() + canvas.clipPath(path) + super.dispatchDraw(canvas) + strokePaint?.let { canvas.drawRoundRect(rectF, radius, radius, it) } + canvas.restoreToCount(save) + } + + private fun resetPath() { + path.reset() + path.addRoundRect(rectF, radius, radius, Path.Direction.CW) + path.close() + } +} diff --git a/uikit/src/main/java/com/sendbird/uikit/internal/ui/widgets/TemplateViews.kt b/uikit/src/main/java/com/sendbird/uikit/internal/ui/widgets/TemplateViews.kt new file mode 100644 index 00000000..ba097ef7 --- /dev/null +++ b/uikit/src/main/java/com/sendbird/uikit/internal/ui/widgets/TemplateViews.kt @@ -0,0 +1,209 @@ +package com.sendbird.uikit.internal.ui.widgets + +import android.annotation.SuppressLint +import android.content.Context +import android.text.TextUtils +import android.util.AttributeSet +import android.view.Gravity +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.ImageView.ScaleType +import android.widget.TextView +import androidx.appcompat.widget.AppCompatImageView +import androidx.appcompat.widget.AppCompatTextView +import com.sendbird.uikit.R +import com.sendbird.uikit.internal.extensions.addRipple +import com.sendbird.uikit.internal.extensions.intToDp +import com.sendbird.uikit.internal.extensions.load +import com.sendbird.uikit.internal.extensions.setAppearance +import com.sendbird.uikit.internal.model.template_messages.BoxViewParams +import com.sendbird.uikit.internal.model.template_messages.ButtonViewParams +import com.sendbird.uikit.internal.model.template_messages.ImageButtonViewParams +import com.sendbird.uikit.internal.model.template_messages.ImageViewParams +import com.sendbird.uikit.internal.model.template_messages.Orientation +import com.sendbird.uikit.internal.model.template_messages.TextViewParams +import com.sendbird.uikit.utils.MetricsUtils + +@SuppressLint("ViewConstructor") +internal open class Text @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : RoundCornerLayout(context, attrs, defStyleAttr) { + val textView: TextView + + init { + layoutParams = LayoutParams( + LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT + ) + textView = AppCompatTextView(context).apply { + // set default button text appearance + setAppearance(context, R.style.SendbirdBody3OnLight01) + ellipsize = TextUtils.TruncateAt.END + layoutParams = LayoutParams( + LayoutParams.MATCH_PARENT, + LayoutParams.MATCH_PARENT + ) + } + this.addView(textView) + } + + fun apply(params: TextViewParams, orientation: Orientation) { + params.viewStyle.apply(this) + params.textStyle.apply(textView) + + gravity = params.align.gravity + params.maxTextLines?.let { textView.maxLines = it } + layoutParams = params.createLayoutParams(context, orientation) + textView.text = params.text + } + + fun setTextAppearance(textAppearance: Int) { + textView.setAppearance(context, textAppearance) + } +} + +@SuppressLint("ViewConstructor") +internal open class Image @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : MessageTemplateImageView(context, attrs, defStyleAttr) { + init { + layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + scaleType = ScaleType.FIT_CENTER + } + + fun apply(params: ImageViewParams, orientation: Orientation) { + layoutParams = params.createLayoutParams(context, orientation) + var resizingSize: Pair? = null + params.metaData?.let { + adjustViewBounds = true + var overrideWidth: Int = it.pixelWidth + var overrideHeight: Int = it.pixelHeight + if (overrideWidth > 0 && overrideHeight > 0) { + val width = MetricsUtils.getScreenSize(context).first + if (overrideWidth > width) { + overrideHeight = (overrideHeight * width.toFloat() / overrideWidth).toInt() + overrideWidth = width + } + resizingSize = Pair(overrideWidth, overrideHeight) + } + } + params.imageStyle.apply(this) + params.viewStyle.apply(this) + load(params.imageUrl, resizingSize) + } +} + +@SuppressLint("ViewConstructor") +internal open class TextButton @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : RoundCornerLayout(context, attrs, defStyleAttr) { + val textView: TextView + + init { + // Even if action doesn't exist click ripple effect should show. (UIKit spec) + layoutParams = LayoutParams( + LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT + ) + isClickable = true + gravity = Gravity.CENTER + // default button padding. + val padding = resources.intToDp(10) + this.setPadding(padding, padding, padding, padding) + this.setBackgroundResource(R.drawable.sb_shape_round_rect_background_200) + setRadiusIntSize(6) + addRipple(background) + + textView = AppCompatTextView(context).apply { + // set default button text appearance + setAppearance(context, R.style.SendbirdButtonPrimary300) + + layoutParams = LayoutParams( + LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT + ) + } + this.addView(textView) + } + + fun apply(params: ButtonViewParams, orientation: Orientation) { + layoutParams = params.createLayoutParams(context, orientation) + params.textStyle.apply(textView) + params.viewStyle.apply(this, true) + textView.maxLines = params.maxTextLines + textView.text = params.text + addRipple(background) + } + + fun setTextAppearance(textAppearance: Int) { + textView.setAppearance(context, textAppearance) + } +} + +@SuppressLint("ViewConstructor") +internal open class ImageButton @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : RoundCornerLayout(context, attrs, defStyleAttr) { + val imageView: ImageView + + init { + // Even if action doesn't exist click ripple effect should show. (UIKit spec) + layoutParams = LayoutParams( + LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT + ) + isClickable = true + setRadiusIntSize(6) + addRipple(background) + + imageView = AppCompatImageView(context).apply { + // set default image content mode. + scaleType = ScaleType.FIT_CENTER + layoutParams = LayoutParams( + LayoutParams.MATCH_PARENT, + LayoutParams.MATCH_PARENT + ) + } + this.addView(imageView) + } + + fun apply(params: ImageButtonViewParams, orientation: Orientation) { + params.imageStyle.apply(imageView) + params.viewStyle.apply(this, true) + layoutParams = params.createLayoutParams(context, orientation) + imageView.load(params.imageUrl) + addRipple(background) + } +} + +@SuppressLint("ViewConstructor") +internal open class Box @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : RoundCornerLayout(context, attrs, defStyleAttr) { + init { + layoutParams = LayoutParams( + LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT + ) + } + + fun apply(params: BoxViewParams, orientation: Orientation) { + this.orientation = params.orientation.value + layoutParams = params.createLayoutParams(context, orientation) + gravity = params.align.gravity + params.viewStyle.apply(this) + } +} diff --git a/uikit/src/main/java/com/sendbird/uikit/log/Logger.java b/uikit/src/main/java/com/sendbird/uikit/log/Logger.java index 62d3dccb..a28babd8 100755 --- a/uikit/src/main/java/com/sendbird/uikit/log/Logger.java +++ b/uikit/src/main/java/com/sendbird/uikit/log/Logger.java @@ -15,7 +15,7 @@ public class Logger { private Logger() { } - private static final int LOG_SEGMENT_SIZE = 2000; + private static final int LOG_SEGMENT_SIZE = 5000; // logLevel should be initialized before loggerConfig initialization private static int logLevel = LoggerConfig.DEV; @NonNull diff --git a/uikit/src/main/java/com/sendbird/uikit/model/Action.java b/uikit/src/main/java/com/sendbird/uikit/model/Action.java new file mode 100644 index 00000000..cc8961bb --- /dev/null +++ b/uikit/src/main/java/com/sendbird/uikit/model/Action.java @@ -0,0 +1,88 @@ +package com.sendbird.uikit.model; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.sendbird.uikit.internal.model.template_messages.ActionData; + +/** + * Custom action data to be linked to a custom message. + * This action data is delivered and used as it is. + * + * @since 3.5.0 + */ +public final class Action { + private final String type; + private final String data; + private final String alterData; + + /** + * Constructor that is used only internally. + * + * @param type an action data type. + * @param data a data delivered and used as it is + * @param alterData an alternative data that can be used if data is not available + * @since 3.5.0 + */ + public Action(@NonNull String type, @NonNull String data, @Nullable String alterData) { + this.type = type; + this.data = data; + this.alterData = alterData; + } + + /** + * Convert ActionData to Action class. This is used only for internal. + * + * @param actionData The data from the given custom data filed. + * @return Action data. + * @since 3.5.0 + */ + @NonNull + public static Action from(@NonNull ActionData actionData) { + return new Action(actionData.getType().name().toLowerCase(), actionData.getData(), actionData.getAlterData()); + } + + /** + * Returns the type of Action. + * "web", "custom", and "uikit" are available. + * + * @return the type of Action. + * @since 3.5.0 + */ + @NonNull + public String getType() { + return type; + } + + /** + * Returns action data that associated with the view. + * + * @return the action data associated with the view. + * @since 3.5.0 + */ + @NonNull + public String getData() { + return data; + } + + /** + * Alternative data that can be used if data is not available + * + * @return the alternative data that can be used if data is not available + * @since 3.5.0 + */ + @Nullable + public String getAlterData() { + return alterData; + } + + @NonNull + @Override + public String toString() { + return "Action{" + + "type='" + type + '\'' + + ", data='" + data + '\'' + + ", alterData='" + alterData + '\'' + + '}'; + } +} diff --git a/uikit/src/main/java/com/sendbird/uikit/model/MessageData.java b/uikit/src/main/java/com/sendbird/uikit/model/MessageData.java new file mode 100644 index 00000000..bfbdbd57 --- /dev/null +++ b/uikit/src/main/java/com/sendbird/uikit/model/MessageData.java @@ -0,0 +1,45 @@ +package com.sendbird.uikit.model; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.sendbird.android.message.BaseMessage; + +import java.util.List; + +/** + * Class that holds message data in a channel. + * + * @since 3.5.0 + */ +public class MessageData { + final List messages; + final String traceName; + + public MessageData(@Nullable String traceName, @NonNull List messages) { + this.traceName = traceName; + this.messages = messages; + } + + /** + * Returns a list of messages for the current channel. + * + * @return A list of the latest messages on the current channel + * @since 3.5.0 + */ + @NonNull + public List getMessages() { + return messages; + } + + /** + * Returns data indicating how the message list was updated. + * + * @return The String that traces the path of the message list + * @since 3.5.0 + */ + @Nullable + public String getTraceName() { + return traceName; + } +} diff --git a/uikit/src/main/java/com/sendbird/uikit/model/MessageListUIParams.java b/uikit/src/main/java/com/sendbird/uikit/model/MessageListUIParams.java index 3c629c4f..14f9160d 100644 --- a/uikit/src/main/java/com/sendbird/uikit/model/MessageListUIParams.java +++ b/uikit/src/main/java/com/sendbird/uikit/model/MessageListUIParams.java @@ -185,13 +185,14 @@ public MessageListUIParams build() { @Override public boolean equals(Object o) { if (this == o) return true; - if (!(o instanceof MessageListUIParams)) return false; + if (o == null || getClass() != o.getClass()) return false; MessageListUIParams that = (MessageListUIParams) o; if (useMessageGroupUI != that.useMessageGroupUI) return false; if (useReverseLayout != that.useReverseLayout) return false; if (useQuotedView != that.useQuotedView) return false; + if (useMessageReceipt != that.useMessageReceipt) return false; return messageGroupType == that.messageGroupType; } @@ -201,6 +202,7 @@ public int hashCode() { result = 31 * result + (useMessageGroupUI ? 1 : 0); result = 31 * result + (useReverseLayout ? 1 : 0); result = 31 * result + (useQuotedView ? 1 : 0); + result = 31 * result + (useMessageReceipt ? 1 : 0); return result; } diff --git a/uikit/src/main/java/com/sendbird/uikit/modules/NotificationChannelModule.java b/uikit/src/main/java/com/sendbird/uikit/modules/NotificationChannelModule.java new file mode 100644 index 00000000..30205220 --- /dev/null +++ b/uikit/src/main/java/com/sendbird/uikit/modules/NotificationChannelModule.java @@ -0,0 +1,255 @@ +package com.sendbird.uikit.modules; + +import android.content.Context; +import android.os.Bundle; +import android.util.TypedValue; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.LinearLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.StyleRes; +import androidx.appcompat.view.ContextThemeWrapper; + +import com.sendbird.uikit.R; +import com.sendbird.uikit.SendbirdUIKit; +import com.sendbird.uikit.interfaces.LoadingDialogHandler; +import com.sendbird.uikit.modules.components.HeaderComponent; +import com.sendbird.uikit.modules.components.NotificationMessageListComponent; +import com.sendbird.uikit.modules.components.StatusComponent; + +/** + * A module for notification channel. + * All composed components are created when the module is created. After than those components can replace. + * + * @since 3.5.0 + */ +public class NotificationChannelModule extends BaseModule { + @NonNull + private final Params params; + @NonNull + private HeaderComponent headerComponent; + @NonNull + private NotificationMessageListComponent messageListComponent; + @NonNull + private StatusComponent statusComponent; + @Nullable + private LoadingDialogHandler loadingDialogHandler; + + /** + * Constructor + * + * @param context The {@code Context} this module is currently associated with + * @since 3.5.0 + */ + public NotificationChannelModule(@NonNull Context context) { + this(context, new Params(context)); + } + + /** + * Constructor + * + * @param context The {@code Context} this module is currently associated with + * @param params The Parameter object that can customize a default Fragment. + * @since 3.5.0 + */ + public NotificationChannelModule(@NonNull Context context, @NonNull Params params) { + this.params = params; + this.headerComponent = new HeaderComponent(); + this.headerComponent.getParams().setUseLeftButton(false); + this.messageListComponent = new NotificationMessageListComponent(); + this.statusComponent = new StatusComponent(); + } + + @Override + @NonNull + public View onCreateView(@NonNull Context context, @NonNull LayoutInflater inflater, @Nullable Bundle args) { + if (args != null) params.apply(context, args); + final Context moduleContext = new ContextThemeWrapper(context, params.getTheme()); + final LinearLayout parent = new LinearLayout(context); + parent.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + parent.setOrientation(LinearLayout.VERTICAL); + + final TypedValue values = new TypedValue(); + if (params.shouldUseHeader()) { + moduleContext.getTheme().resolveAttribute(R.attr.sb_component_header, values, true); + final Context headerThemeContext = new ContextThemeWrapper(moduleContext, values.resourceId); + final LayoutInflater headerInflater = inflater.cloneInContext(headerThemeContext); + final View header = this.headerComponent.onCreateView(headerThemeContext, headerInflater, parent, args); + parent.addView(header); + } + final FrameLayout innerContainer = new FrameLayout(context); + innerContainer.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + parent.addView(innerContainer); + + moduleContext.getTheme().resolveAttribute(R.attr.sb_component_list, values, true); + final Context listThemeContext = new ContextThemeWrapper(moduleContext, values.resourceId); + final LayoutInflater listInflater = inflater.cloneInContext(listThemeContext); + final View channelListLayout = this.messageListComponent.onCreateView(listThemeContext, listInflater, innerContainer, args); + innerContainer.addView(channelListLayout); + + moduleContext.getTheme().resolveAttribute(R.attr.sb_component_status, values, true); + final Context statusThemeContext = new ContextThemeWrapper(moduleContext, values.resourceId); + final LayoutInflater statusInflater = inflater.cloneInContext(statusThemeContext); + final View statusLayout = statusComponent.onCreateView(statusThemeContext, statusInflater, innerContainer, args); + innerContainer.addView(statusLayout); + return parent; + } + + /** + * Sets the handler for the loading dialog. + * + * @param loadingDialogHandler Loading dialog handler to be used in this module + * @since 3.5.0 + */ + public void setOnLoadingDialogHandler(@Nullable LoadingDialogHandler loadingDialogHandler) { + this.loadingDialogHandler = loadingDialogHandler; + } + + /** + * Returns the handler for loading dialog. + * + * @return Loading dialog handler to be used in this module + * @since 3.5.0 + */ + @Nullable + public LoadingDialogHandler getLoadingDialogHandler() { + return loadingDialogHandler; + } + + /** + * Sets a custom notification channel header component. + * + * @param component The channel header component to be used in this module + * @since 3.5.0 + */ + public void setHeaderComponent(@NonNull T component) { + this.headerComponent = component; + } + + /** + * Sets a custom notification message list component. + * + * @param component The notification message list component to be used in this module + * @since 3.5.0 + */ + public void setMessageListComponent(@NonNull NotificationMessageListComponent component) { + this.messageListComponent = component; + } + + /** + * Sets a custom status component. + * + * @param component The status component to be used in this module + * @since 3.5.0 + */ + public void setStatusComponent(@NonNull T component) { + this.statusComponent = component; + } + + /** + * Returns the notification channel header component. + * + * @return The channel header component of this module + * @since 3.5.0 + */ + @NonNull + public HeaderComponent getHeaderComponent() { + return headerComponent; + } + + /** + * Returns the notification message list component. + * + * @return The notification message list component of this module + * @since 3.5.0 + */ + @NonNull + public NotificationMessageListComponent getMessageListComponent() { + return messageListComponent; + } + + /** + * Returns the status component. + * + * @return The status component of this module + * @since 3.5.0 + */ + @NonNull + public StatusComponent getStatusComponent() { + return statusComponent; + } + + /** + * It will be called when the loading dialog needs displaying. + * + * @return True if the callback has consumed the event, false otherwise. + * @since 3.5.0 + */ + public boolean shouldShowLoadingDialog() { + if (loadingDialogHandler != null && loadingDialogHandler.shouldShowLoadingDialog()) { + return true; + } + // Do nothing on the channel. + return false; + } + + /** + * It will be called when the loading dialog needs dismissing. + * + * @since 3.5.0 + */ + public void shouldDismissLoadingDialog() { + if (loadingDialogHandler != null) { + loadingDialogHandler.shouldDismissLoadingDialog(); + } + } + + /** + * Returns a collection of parameters applied to this module. + * + * @return {@link Params} applied to this module. + * @since 3.5.0 + */ + @NonNull + public Params getParams() { + return params; + } + + public static class Params extends BaseModule.Params { + /** + * Constructor + * + * @param context The {@code Context} this module is currently associated with + * @since 3.5.0 + */ + public Params(@NonNull Context context) { + this(context, SendbirdUIKit.getDefaultThemeMode()); + } + + /** + * Constructor + * + * @param context The {@code Context} this module is currently associated with + * @param themeMode The theme of Sendbird UIKit to be applied to this module + * @since 3.5.0 + */ + public Params(@NonNull Context context, @NonNull SendbirdUIKit.ThemeMode themeMode) { + super(context, themeMode, R.attr.sb_module_notification_channel); + } + + /** + * Constructor + * + * @param context The {@code Context} this module is currently associated with + * @param themeResId The theme resource ID to be applied to this module + * @since 3.5.0 + */ + public Params(@NonNull Context context, @StyleRes int themeResId) { + super(context, themeResId, R.attr.sb_module_notification_channel); + } + } +} diff --git a/uikit/src/main/java/com/sendbird/uikit/modules/components/MessageListComponent.java b/uikit/src/main/java/com/sendbird/uikit/modules/components/MessageListComponent.java index c7c6c887..eed2ad99 100644 --- a/uikit/src/main/java/com/sendbird/uikit/modules/components/MessageListComponent.java +++ b/uikit/src/main/java/com/sendbird/uikit/modules/components/MessageListComponent.java @@ -1,14 +1,11 @@ package com.sendbird.uikit.modules.components; -import android.content.Context; -import android.os.Bundle; -import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.sendbird.android.channel.GroupChannel; import com.sendbird.android.message.BaseMessage; import com.sendbird.android.message.SendingStatus; import com.sendbird.uikit.activities.adapter.MessageListAdapter; @@ -49,14 +46,12 @@ public Params getParams() { return (Params) super.getParams(); } - @NonNull @Override - public View onCreateView(@NonNull Context context, @NonNull LayoutInflater inflater, @NonNull ViewGroup parent, @Nullable Bundle args) { - final View view = super.onCreateView(context, inflater, parent, args); + public void notifyChannelChanged(@NonNull GroupChannel channel) { if (getAdapter() == null) { - setAdapter(new MessageListAdapter(null, getParams().shouldUseGroupUI())); + setAdapter(new MessageListAdapter(channel, getParams().shouldUseGroupUI())); } - return view; + super.notifyChannelChanged(channel); } @Override diff --git a/uikit/src/main/java/com/sendbird/uikit/modules/components/NotificationMessageListComponent.java b/uikit/src/main/java/com/sendbird/uikit/modules/components/NotificationMessageListComponent.java new file mode 100644 index 00000000..b295a895 --- /dev/null +++ b/uikit/src/main/java/com/sendbird/uikit/modules/components/NotificationMessageListComponent.java @@ -0,0 +1,258 @@ +package com.sendbird.uikit.modules.components; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.LinearLayoutManager; + +import com.sendbird.android.channel.GroupChannel; +import com.sendbird.android.message.BaseMessage; +import com.sendbird.uikit.R; +import com.sendbird.uikit.activities.adapter.NotificationMessageListAdapter; +import com.sendbird.uikit.consts.StringSet; +import com.sendbird.uikit.fragments.ItemAnimator; +import com.sendbird.uikit.interfaces.OnMessageListUpdateHandler; +import com.sendbird.uikit.interfaces.OnMessageTemplateActionHandler; +import com.sendbird.uikit.interfaces.OnPagedDataLoader; +import com.sendbird.uikit.internal.ui.widgets.InnerLinearLayoutManager; +import com.sendbird.uikit.internal.ui.widgets.PagerRecyclerView; +import com.sendbird.uikit.model.Action; + +import java.util.List; + +/** + * This class creates and performs a view corresponding the notification message list area in Sendbird UIKit. + * + * @since 3.5.0 + */ +public class NotificationMessageListComponent { + @NonNull + private final Params params; + @Nullable + private PagerRecyclerView pagerRecyclerView; + @Nullable + private NotificationMessageListAdapter adapter; + @Nullable + private OnMessageTemplateActionHandler onMessageTemplateActionHandler; + @Nullable + private OnPagedDataLoader> pagedDataLoader; + + /** + * Constructor + * + * @since 3.5.0 + */ + public NotificationMessageListComponent() { + this.params = new Params(); + } + + /** + * Returns a collection of parameters applied to this component. + * + * @return {@code Params} applied to this component + * @since 3.5.0 + */ + @NonNull + public Params getParams() { + return params; + } + + /** + * Returns the view created by {@link #onCreateView(Context, LayoutInflater, ViewGroup, Bundle)}. + * + * @return the topmost view containing this view + * @since 3.5.0 + */ + @Nullable + public View getRootView() { + return this.pagerRecyclerView; + } + + /** + * Returns the message list adapter. + * + * @return The adapter applied to this list component + * @since 3.5.0 + */ + @Nullable + public NotificationMessageListAdapter getAdapter() { + return adapter; + } + + /** + * Called after the component was created to make views. + *

If this function is used override, {@link #getRootView()} must also be override.

+ * + * @param context The {@code Context} this component is currently associated with + * @param inflater The LayoutInflater object that can be used to inflate any views in the component + * @param parent The ViewGroup into which the new View will be added + * @param args The arguments supplied when the component was instantiated, if any + * @return Return the View for the UI. + * @since 3.5.0 + */ + @NonNull + public View onCreateView(@NonNull Context context, @NonNull LayoutInflater inflater, @NonNull ViewGroup parent, @Nullable Bundle args) { + if (args != null) params.apply(context, args); + + this.pagerRecyclerView = new PagerRecyclerView(context, null, R.attr.sb_component_list); + pagerRecyclerView.setHasFixedSize(true); + pagerRecyclerView.setClipToPadding(false); + pagerRecyclerView.setThreshold(5); + pagerRecyclerView.setUseDivider(false); + pagerRecyclerView.setItemAnimator(new ItemAnimator()); + + final LinearLayoutManager layoutManager = new InnerLinearLayoutManager(pagerRecyclerView.getContext()); + layoutManager.setReverseLayout(false); + pagerRecyclerView.setLayoutManager(layoutManager); + return this.pagerRecyclerView; + } + + /** + * Sets the paged data loader for message list. + * + * @param pagedDataLoader The paged data loader to be applied to this list component + * @since 3.5.0 + */ + public void setPagedDataLoader(@NonNull OnPagedDataLoader> pagedDataLoader) { + this.pagedDataLoader = pagedDataLoader; + if (pagerRecyclerView != null) + pagerRecyclerView.setPager(pagedDataLoader); + } + + /** + * Sets the message list adapter to provide child views on demand. + *

When adapter is changed, all existing views are recycled back to the pool. If the pool has only one adapter, it will be cleared.

+ * + * @param adapter The adapter to be applied to this list component + * @since 3.5.0 + */ + public void setAdapter(@NonNull NotificationMessageListAdapter adapter) { + this.adapter = adapter; + if (pagerRecyclerView == null) return; + if (this.adapter.getOnMessageTemplateActionHandler() == null) { + this.adapter.setOnMessageTemplateActionHandler(this::onMessageTemplateActionClicked); + } + pagerRecyclerView.setAdapter(this.adapter); + } + + /** + * Register a callback to be invoked when the view that has an {@link com.sendbird.uikit.model.Action} data is clicked. + * If an Action is registered in a specific view, it is called when a click event occurs. + * + * @param handler The callback that will run + * @since 3.5.0 + */ + public void setOnMessageTemplateActionHandler(@Nullable OnMessageTemplateActionHandler handler) { + this.onMessageTemplateActionHandler = handler; + } + + /** + * Called when the view that has an {@link com.sendbird.uikit.model.Action} data is clicked. + * + * @param view the view that was clicked. + * @param action the registered Action data + * @param message the clicked message + * @since 3.5.0 + */ + protected void onMessageTemplateActionClicked(@NonNull View view, @NonNull Action action, @NonNull BaseMessage message) { + if (onMessageTemplateActionHandler != null) onMessageTemplateActionHandler.onHandleAction(view, action, message); + } + + /** + * Handles a new channel when data has changed. + * + * @param channel The latest group channel + * @since 3.5.0 + */ + public void notifyChannelChanged(@NonNull GroupChannel channel) { + if (getAdapter() == null) { + setAdapter(new NotificationMessageListAdapter(channel, params.shouldDisplayUserProfile)); + } + } + + /** + * Handles the data needed to draw the message list has changed. + * + * @param messageList The list of messages to be drawn + * @param channel The latest group channel + * @param callback Callback when the message list is updated + * @since 3.5.0 + */ + public void notifyDataSetChanged(@NonNull List messageList, @NonNull GroupChannel channel, @Nullable OnMessageListUpdateHandler callback) { + if (this.pagerRecyclerView == null) return; + if (this.adapter != null) { + adapter.setItems(channel, messageList, callback); + } + } + + /** + * Sets the last seen timestamp to update new badge UI. + * This value is used to compare whether a message has been newly received. + * + * @param lastSeenAt the timestamp last viewed by the user. + * @since 3.5.0 + */ + @SuppressLint("NotifyDataSetChanged") + public void notifyLastSeenUpdated(long lastSeenAt) { + if (this.adapter != null) { + adapter.updateLastSeenAt(lastSeenAt); + adapter.notifyDataSetChanged(); + } + } + + /** + * A collection of parameters, which can be applied to a default View. The values of params are not dynamically applied at runtime. + * Params cannot be created directly, and it is automatically created together when components are created. + *

Since the onCreateView configuring View uses the values of the set Params, we recommend that you set up for Params before the onCreateView is called.

+ * + * @see #getParams() + * @since 3.5.0 + */ + public static class Params { + private boolean shouldDisplayUserProfile = true; + + /** + * Sets whether to display the user profile when drawing a message. + * + * @param shouldDisplayUserProfile true if the user profile is shown, false otherwise + * @since 3.5.0 + */ + public void setDisplayUserProfile(boolean shouldDisplayUserProfile) { + this.shouldDisplayUserProfile = shouldDisplayUserProfile; + } + + /** + * Returns whether to display the user profile when drawing a message. + * + * @return true if the user profile is shown, false otherwise + * @since 3.5.0 + */ + public boolean shouldDisplayUserProfile() { + return shouldDisplayUserProfile; + } + + /** + * Apply data that matches keys mapped to Params' properties. + * {@code KEY_USE_USER_PROFILE} is mapped to {@link #setDisplayUserProfile(boolean)} + * + * @param context The {@code Context} this component is currently associated with + * @param args The sets of arguments to apply at Params. + * @return This Params object that applied with given data. + * @since 3.5.0 + */ + @NonNull + protected Params apply(@NonNull Context context, @NonNull Bundle args) { + if (args.containsKey(StringSet.KEY_SHOULD_DISPLAY_USER_PROFILE)) { + setDisplayUserProfile(args.getBoolean(StringSet.KEY_SHOULD_DISPLAY_USER_PROFILE)); + } + return this; + } + } + +} diff --git a/uikit/src/main/java/com/sendbird/uikit/utils/DrawableUtils.java b/uikit/src/main/java/com/sendbird/uikit/utils/DrawableUtils.java index dd5c8099..8515774c 100644 --- a/uikit/src/main/java/com/sendbird/uikit/utils/DrawableUtils.java +++ b/uikit/src/main/java/com/sendbird/uikit/utils/DrawableUtils.java @@ -16,6 +16,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.content.res.AppCompatResources; +import androidx.core.content.ContextCompat; import androidx.core.graphics.drawable.DrawableCompat; import com.sendbird.uikit.R; @@ -72,6 +73,13 @@ public static Drawable setTintList(@Nullable Drawable drawable, @Nullable ColorS return mutated; } + @NonNull + public static Drawable createOvalIcon(@NonNull Context context, @ColorRes int color) { + final ShapeDrawable drawable = new ShapeDrawable(new OvalShape()); + drawable.getPaint().setColor(ContextCompat.getColor(context, color)); + return drawable; + } + @NonNull public static Drawable createOvalIcon(@NonNull Context context, @ColorRes int backgroundColor, @DrawableRes int iconRes, @ColorRes int iconTint) { @@ -90,6 +98,15 @@ public static Drawable createOvalIcon(@NonNull Context context, @ColorRes int ba } @NonNull + public static Drawable createOvalIconWithInset(@NonNull Context context, @ColorRes int background, @DrawableRes int iconRes, @ColorRes int iconTint, int inset) { + final ShapeDrawable drawable = new ShapeDrawable(new OvalShape()); + drawable.getPaint().setColor(ContextCompat.getColor(context, background)); + final Drawable icon = setTintList(context, iconRes, iconTint); + return createLayerIcon(drawable, icon, inset); + } + + + @NonNull public static Drawable createLayerIcon(@Nullable Drawable background, @Nullable Drawable icon, int inset) { Drawable[] layer = {background, icon}; LayerDrawable layerDrawable = new LayerDrawable(layer); diff --git a/uikit/src/main/java/com/sendbird/uikit/utils/ViewUtils.java b/uikit/src/main/java/com/sendbird/uikit/utils/ViewUtils.java index 08f9e601..e04fe32f 100644 --- a/uikit/src/main/java/com/sendbird/uikit/utils/ViewUtils.java +++ b/uikit/src/main/java/com/sendbird/uikit/utils/ViewUtils.java @@ -239,6 +239,15 @@ public static void drawNickname(@NonNull TextView tvNickname, @Nullable BaseMess tvNickname.setText(nickname); } + public static void drawNotificationProfile(@NonNull ImageView ivProfile, @Nullable BaseMessage message) { + int iconTint = SendbirdUIKit.isDarkMode() ? R.color.onlight_01 : R.color.ondark_01; + int backgroundTint = R.color.background_300; + int inset = ivProfile.getContext().getResources().getDimensionPixelSize(R.dimen.sb_size_6); + final Drawable profile = DrawableUtils.createOvalIconWithInset(ivProfile.getContext(), + backgroundTint, R.drawable.icon_channels, iconTint, inset); + ivProfile.setImageDrawable(profile); + } + public static void drawProfile(@NonNull ImageView ivProfile, @Nullable BaseMessage message) { if (message == null) { return; diff --git a/uikit/src/main/java/com/sendbird/uikit/vm/ChannelListViewModel.java b/uikit/src/main/java/com/sendbird/uikit/vm/ChannelListViewModel.java index 241904cd..c9b6f183 100644 --- a/uikit/src/main/java/com/sendbird/uikit/vm/ChannelListViewModel.java +++ b/uikit/src/main/java/com/sendbird/uikit/vm/ChannelListViewModel.java @@ -99,6 +99,7 @@ private synchronized void disposeChannelCollection() { private void notifyChannelChanged() { if (collection == null) return; List newList = collection.getChannelList(); + Logger.d(">> ChannelListViewModel::notifyDataSetChanged(), size = %s", newList.size()); channelList.postValue(newList); } diff --git a/uikit/src/main/java/com/sendbird/uikit/vm/NotificationChannelViewModel.java b/uikit/src/main/java/com/sendbird/uikit/vm/NotificationChannelViewModel.java new file mode 100644 index 00000000..ba1d0d8f --- /dev/null +++ b/uikit/src/main/java/com/sendbird/uikit/vm/NotificationChannelViewModel.java @@ -0,0 +1,436 @@ +package com.sendbird.uikit.vm; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.UiThread; +import androidx.annotation.WorkerThread; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleEventObserver; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; + +import com.sendbird.android.SendbirdChat; +import com.sendbird.android.channel.GroupChannel; +import com.sendbird.android.collection.GroupChannelContext; +import com.sendbird.android.collection.MessageCollection; +import com.sendbird.android.collection.MessageCollectionInitPolicy; +import com.sendbird.android.collection.MessageContext; +import com.sendbird.android.collection.Traceable; +import com.sendbird.android.exception.SendbirdException; +import com.sendbird.android.handler.MessageCollectionHandler; +import com.sendbird.android.handler.MessageCollectionInitHandler; +import com.sendbird.android.message.BaseMessage; +import com.sendbird.android.params.MessageCollectionCreateParams; +import com.sendbird.android.params.MessageListParams; +import com.sendbird.uikit.consts.StringSet; +import com.sendbird.uikit.interfaces.AuthenticateHandler; +import com.sendbird.uikit.interfaces.OnCompleteHandler; +import com.sendbird.uikit.interfaces.OnPagedDataLoader; +import com.sendbird.uikit.log.Logger; +import com.sendbird.uikit.model.LiveDataEx; +import com.sendbird.uikit.model.MessageData; +import com.sendbird.uikit.model.MutableLiveDataEx; +import com.sendbird.uikit.widgets.StatusFrameView; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; + +/** + * ViewModel preparing and managing data related with the notification channel. + * + * @since 3.5.0 + */ +public class NotificationChannelViewModel extends BaseViewModel implements OnPagedDataLoader>, LifecycleEventObserver { + @NonNull + private final MutableLiveData channelUpdated = new MutableLiveData<>(); + @NonNull + private final MutableLiveData channelDeleted = new MutableLiveData<>(); + @NonNull + private final MutableLiveData> messagesDeleted = new MutableLiveData<>(); + @NonNull + private final MutableLiveData statusFrame = new MutableLiveData<>(); + @NonNull + private final MutableLiveDataEx messageList = new MutableLiveDataEx<>(); + @Nullable + private MessageListParams messageListParams; + @Nullable + private MessageCollection collection; + @NonNull + private final String channelUrl; + @Nullable + private GroupChannel channel; + + private boolean isVisible = false; + + /** + * Constructor + * + * @param channelUrl The URL of a channel this view model is currently associated with + * @param messageListParams Parameters required to retrieve the message list from this view model + * @since 3.5.0 + */ + public NotificationChannelViewModel(@NonNull String channelUrl, @Nullable MessageListParams messageListParams) { + this.channelUrl = channelUrl; + this.messageListParams = messageListParams; + } + + /** + * Tries to connect Sendbird Server and retrieve a channel instance. + * + * @param handler Callback notifying the result of authentication + * @since 3.5.0 + */ + @Override + public void authenticate(@NonNull AuthenticateHandler handler) { + connect((user, e) -> { + if (user != null) { + GroupChannel.getChannel(channelUrl, (channel, e1) -> { + NotificationChannelViewModel.this.channel = channel; + if (e1 != null) { + handler.onAuthenticationFailed(); + } else { + handler.onAuthenticated(); + } + }); + } else { + handler.onAuthenticationFailed(); + } + }); + } + + @Override + protected void onCleared() { + super.onCleared(); + Logger.d("-- onCleared NotificationChannelViewModel"); + disposeMessageCollection(); + } + + /** + * Returns {@code GroupChannel}. If the authentication failed, {@code null} is returned. + * + * @return {@code GroupChannel} this view model is currently associated with + * @since 3.5.0 + */ + @Nullable + public GroupChannel getChannel() { + return channel; + } + + /** + * Returns parameters required to retrieve the message list from this view model + * + * @return {@link MessageListParams} used in this view model + * @since 3.5.0 + */ + @Nullable + public MessageListParams getMessageListParams() { + return messageListParams; + } + + /** + * Returns URL of GroupChannel. + * + * @return The URL of a channel this view model is currently associated with + * @since 3.5.0 + */ + @NonNull + public String getChannelUrl() { + return channelUrl; + } + + /** + * Returns LiveData that can be observed for the list of messages. + * + * @return LiveData holding the latest {@link ChannelViewModel.ChannelMessageData} + * @since 3.5.0 + */ + @NonNull + public LiveDataEx getMessageList() { + return messageList; + } + + /** + * Returns LiveData that can be observed for the status of the result of fetching the message list. + * When the message list is fetched successfully, the status is {@link StatusFrameView.Status#NONE}. + * + * @return The Status for the message list + * @since 3.5.0 + */ + @NonNull + public MutableLiveData getStatusFrame() { + return statusFrame; + } + + /** + * Returns LiveData that can be observed if the channel has been deleted. + * + * @return LiveData holding the URL of the deleted {@code GroupChannel} + * @since 3.5.0 + */ + @NonNull + public LiveData onChannelDeleted() { + return channelDeleted; + } + + /** + * Requests the list of BaseMessages for the first time. + * If there is no more pages to be read, an empty List (not null) returns. + * If the request is succeed, you can observe updated data through {@link #getMessageList()}. + * + * @param startingPoint Timestamp that is the starting point when the message list is fetched + * @since 3.5.0 + */ + @UiThread + public synchronized boolean loadInitial(final long startingPoint) { + Logger.d(">> NotificationChannelViewModel::loadInitial() startingPoint=%s", startingPoint); + initMessageCollection(startingPoint); + if (collection == null) { + Logger.d("-- channel instance is null. an authenticate process must be proceed first"); + return false; + } + + collection.initialize(MessageCollectionInitPolicy.CACHE_AND_REPLACE_BY_API, new MessageCollectionInitHandler() { + @Override + public void onCacheResult(@Nullable List cachedList, @Nullable SendbirdException e) { + if (e == null && cachedList != null && cachedList.size() > 0) { + notifyDataSetChanged(StringSet.ACTION_INIT_FROM_CACHE); + } + } + + @Override + public void onApiResult(@Nullable List apiResultList, @Nullable SendbirdException e) { + if (e == null && apiResultList != null) { + notifyDataSetChanged(StringSet.ACTION_INIT_FROM_REMOTE); + } + } + }); + return true; + } + + /** + * Requests the list of BaseMessages when the page goes to the previous. + * If there is no more pages to be read, an empty List (not null) returns. + * If the request is succeed, you can observe updated data through {@link #getMessageList()}. + * + * @return Returns the list of BaseMessages if no error occurs + * @throws Exception Throws exception if getting the message list are failed + * @since 3.5.0 + */ + @WorkerThread + @NonNull + @Override + public List loadPrevious() throws Exception { + if (!hasPrevious() || collection == null) return Collections.emptyList(); + Logger.i(">> NotificationChannelViewModel::loadPrevious()"); + + final AtomicReference> result = new AtomicReference<>(); + final AtomicReference error = new AtomicReference<>(); + final CountDownLatch lock = new CountDownLatch(1); + + collection.loadPrevious((messages, e) -> { + try { + if (e == null) { + messages = messages == null ? Collections.emptyList() : messages; + result.set(messages); + notifyDataSetChanged(StringSet.ACTION_PREVIOUS); + } + error.set(e); + } finally { + lock.countDown(); + } + }); + lock.await(); + + if (error.get() != null) throw error.get(); + return result.get(); + } + + /** + * Requests the list of BaseMessages when the page goes to the next. + * If there is no more pages to be read, an empty List (not null) returns. + * If the request is succeed, you can observe updated data through {@link #getMessageList()}. + * + * @return Returns the list of BaseMessages if no error occurs + * @throws Exception Throws exception if getting the message list are failed + * @since 3.5.0 + */ + @WorkerThread + @NonNull + @Override + public List loadNext() throws Exception { + return Collections.emptyList(); + } + + @Override + public boolean hasNext() { + return false; + } + + @Override + public boolean hasPrevious() { + return collection == null || collection.getHasPrevious(); + } + + @UiThread + synchronized void notifyDataSetChanged(@NonNull Traceable trace) { + notifyDataSetChanged(trace.getTraceName()); + } + + @UiThread + private synchronized void notifyChannelDataChanged() { + Logger.d(">> NotificationChannelViewModel::notifyChannelDataChanged()"); + channelUpdated.setValue(channel); + } + + @UiThread + private synchronized void notifyDataSetChanged(@NonNull String traceName) { + Logger.d(">> NotificationChannelViewModel::notifyDataSetChanged()"); + if (collection == null) return; + final List copiedList = collection.getSucceededMessages(); + if (copiedList.size() == 0) { + statusFrame.setValue(StatusFrameView.Status.EMPTY); + } else { + statusFrame.setValue(StatusFrameView.Status.NONE); + messageList.setValue(new MessageData(traceName, copiedList)); + } + } + + @UiThread + private synchronized void notifyMessagesDeleted(@NonNull List deletedMessages) { + messagesDeleted.setValue(deletedMessages); + } + + @UiThread + private synchronized void notifyChannelDeleted(@NonNull String channelUrl) { + channelDeleted.setValue(channelUrl); + } + + private synchronized void initMessageCollection(final long startingPoint) { + Logger.i(">> NotificationChannelViewModel::initMessageCollection()"); + final GroupChannel channel = getChannel(); + if (channel == null) return; + if (this.collection != null) { + disposeMessageCollection(); + } + if (this.messageListParams == null) { + this.messageListParams = createMessageListParams(); + } + this.messageListParams.setReverse(true); + this.collection = SendbirdChat.createMessageCollection(new MessageCollectionCreateParams(channel, this.messageListParams, startingPoint, new MessageCollectionHandler() { + @UiThread + @Override + public void onMessagesAdded(@NonNull MessageContext context, @NonNull GroupChannel channel, @NonNull List messages) { + Logger.d(">> NotificationChannelViewModel::onMessagesAdded() from=%s", context.getCollectionEventSource()); + if (messages.isEmpty()) return; + + switch (context.getCollectionEventSource()) { + case EVENT_MESSAGE_RECEIVED: + case EVENT_MESSAGE_SENT: + case MESSAGE_FILL: + if (isVisible) markAsRead(); + break; + } + notifyDataSetChanged(context); + } + + @UiThread + @Override + public void onMessagesUpdated(@NonNull MessageContext context, @NonNull GroupChannel channel, @NonNull List messages) { + Logger.d(">> NotificationChannelViewModel::onMessagesUpdated() from=%s", context.getCollectionEventSource()); + notifyDataSetChanged(context); + } + + @UiThread + @Override + public void onMessagesDeleted(@NonNull MessageContext context, @NonNull GroupChannel channel, @NonNull List messages) { + Logger.d(">> NotificationChannelViewModel::onMessagesDeleted() from=%s", context.getCollectionEventSource()); + // Remove the succeeded message from the succeeded message datasource. + notifyMessagesDeleted(messages); + notifyDataSetChanged(context); + } + + @UiThread + @Override + public void onChannelDeleted(@NonNull GroupChannelContext context, @NonNull String channelUrl) { + Logger.d(">> NotificationChannelViewModel::onChannelDeleted() from=%s", context.getCollectionEventSource()); + notifyChannelDeleted(channelUrl); + } + + @UiThread + @Override + public void onHugeGapDetected() { + Logger.d(">> NotificationChannelViewModel::onHugeGapDetected()"); + } + + @Override + public void onChannelUpdated(@NonNull GroupChannelContext context, @NonNull GroupChannel channel) { + Logger.d(">> NotificationChannelViewModel::onChannelUpdated() from=%s, url=%s", context.getCollectionEventSource(), channel.getUrl()); + notifyChannelDataChanged(); + } + })); + } + + private synchronized void disposeMessageCollection() { + Logger.i(">> NotificationChannelViewModel::disposeMessageCollection()"); + if (this.collection != null) { + this.collection.setMessageCollectionHandler(null); + this.collection.dispose(); + } + } + + public void markAsRead() { + Logger.d(">> NotificationChannelViewModel::markAsRead()"); + if (channel != null) channel.markAsRead(null); + } + + /** + * Deletes a message. + * + * @param message Message to be deleted + * @param handler Callback handler called when this method is completed + * @since 3.5.0 + */ + public void deleteMessage(@NonNull BaseMessage message, @Nullable OnCompleteHandler handler) { + if (channel == null) return; + channel.deleteMessage(message, e -> { + if (handler != null) handler.onComplete(e); + Logger.i("++ deleted message : %s", message); + }); + } + + /** + * Creates params for the message list when loading the message list. + * + * @return {@link MessageListParams} to be used when loading the message list + * @since 3.5.0 + */ + @NonNull + public MessageListParams createMessageListParams() { + final MessageListParams params = new MessageListParams(); + params.setReverse(true); + return params; + } + + /** + * Called when a state transition event happens. + * + * @param source The source of the event + * @param event The event + */ + @Override + public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { + Logger.i(">> NotificationChannelViewModel::onStateChanged(%s)", event); + switch (event) { + case ON_RESUME: + isVisible = true; + markAsRead(); + break; + case ON_PAUSE: + isVisible = false; + break; + } + } +} diff --git a/uikit/src/main/java/com/sendbird/uikit/vm/ViewModelFactory.java b/uikit/src/main/java/com/sendbird/uikit/vm/ViewModelFactory.java index 8d1a46a1..ee918b11 100644 --- a/uikit/src/main/java/com/sendbird/uikit/vm/ViewModelFactory.java +++ b/uikit/src/main/java/com/sendbird/uikit/vm/ViewModelFactory.java @@ -39,6 +39,8 @@ public T create(@NonNull Class modelClass) { return (T) new ChannelViewModel((String) Objects.requireNonNull(params)[0], params.length > 1 ? (MessageListParams) params[1] : null); } else if (modelClass.isAssignableFrom(ChannelListViewModel.class)) { return (T) new ChannelListViewModel(params != null && params.length > 0 ? (GroupChannelListQuery) params[0] : null); + } else if (modelClass.isAssignableFrom(NotificationChannelViewModel.class)) { + return (T) new NotificationChannelViewModel((String) Objects.requireNonNull(params)[0], params.length > 1 ? (MessageListParams) params[1] : null); } else if (modelClass.isAssignableFrom(OpenChannelViewModel.class)) { return (T) new OpenChannelViewModel((String) Objects.requireNonNull(params)[0], params.length > 1 ? (MessageListParams) params[1] : null); } else if (modelClass.isAssignableFrom(OpenChannelSettingsViewModel.class)) { diff --git a/uikit/src/main/java/com/sendbird/uikit/widgets/MessageInputView.kt b/uikit/src/main/java/com/sendbird/uikit/widgets/MessageInputView.kt index c79a9e49..0847839a 100644 --- a/uikit/src/main/java/com/sendbird/uikit/widgets/MessageInputView.kt +++ b/uikit/src/main/java/com/sendbird/uikit/widgets/MessageInputView.kt @@ -352,10 +352,10 @@ class MessageInputView @JvmOverloads constructor( } }) binding.etInputText.inputType = ( - InputType.TYPE_CLASS_TEXT - or InputType.TYPE_TEXT_FLAG_MULTI_LINE - or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES - ) + InputType.TYPE_CLASS_TEXT + or InputType.TYPE_TEXT_FLAG_MULTI_LINE + or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES + ) } finally { a.recycle() } diff --git a/uikit-custom-sample/src/main/res/drawable/shape_round_rect_background_100.xml b/uikit/src/main/res/drawable/sb_shape_round_rect_background_200.xml similarity index 59% rename from uikit-custom-sample/src/main/res/drawable/shape_round_rect_background_100.xml rename to uikit/src/main/res/drawable/sb_shape_round_rect_background_200.xml index d3afa3e5..0649f9a6 100644 --- a/uikit-custom-sample/src/main/res/drawable/shape_round_rect_background_100.xml +++ b/uikit/src/main/res/drawable/sb_shape_round_rect_background_200.xml @@ -3,7 +3,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> - + - - \ No newline at end of file + + diff --git a/uikit-custom-sample/src/main/res/drawable/shape_round_rect_primary_300.xml b/uikit/src/main/res/drawable/sb_shape_round_rect_background_400.xml similarity index 59% rename from uikit-custom-sample/src/main/res/drawable/shape_round_rect_primary_300.xml rename to uikit/src/main/res/drawable/sb_shape_round_rect_background_400.xml index 399c1767..188ced2a 100644 --- a/uikit-custom-sample/src/main/res/drawable/shape_round_rect_primary_300.xml +++ b/uikit/src/main/res/drawable/sb_shape_round_rect_background_400.xml @@ -3,7 +3,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> - + - - \ No newline at end of file + + diff --git a/uikit/src/main/res/layout/sb_view_message_notification_channel.xml b/uikit/src/main/res/layout/sb_view_message_notification_channel.xml new file mode 100644 index 00000000..fe277c49 --- /dev/null +++ b/uikit/src/main/res/layout/sb_view_message_notification_channel.xml @@ -0,0 +1,6 @@ + + diff --git a/uikit/src/main/res/layout/sb_view_message_notification_channel_component.xml b/uikit/src/main/res/layout/sb_view_message_notification_channel_component.xml new file mode 100644 index 00000000..3fbefaf8 --- /dev/null +++ b/uikit/src/main/res/layout/sb_view_message_notification_channel_component.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + diff --git a/uikit/src/main/res/values/attrs.xml b/uikit/src/main/res/values/attrs.xml index fb338ca3..3a8802ac 100644 --- a/uikit/src/main/res/values/attrs.xml +++ b/uikit/src/main/res/values/attrs.xml @@ -26,6 +26,7 @@ + @@ -112,6 +113,8 @@ + + @@ -294,6 +297,21 @@ + + + ` + + + + + + + + + + + + diff --git a/uikit/src/main/res/values/strings.xml b/uikit/src/main/res/values/strings.xml index 1f3e9ce6..3dce656a 100644 --- a/uikit/src/main/res/values/strings.xml +++ b/uikit/src/main/res/values/strings.xml @@ -47,6 +47,9 @@ 99 No messages Message unavailable + (Message template error)\nCan\'t read this message. + No notifications + Something went wrong. Moderations diff --git a/uikit/src/main/res/values/styles.xml b/uikit/src/main/res/values/styles.xml index b291378b..10acc7f4 100755 --- a/uikit/src/main/res/values/styles.xml +++ b/uikit/src/main/res/values/styles.xml @@ -31,6 +31,7 @@ @style/Module.CreateOpenChannel @style/Module.OpenChannelList @style/Module.MessageThread + @style/Module.NotificationChannel @style/Component.ChannelMessageInput @style/Component.ChannelSettingsInfo @@ -169,6 +170,11 @@ @style/Component.MessageThreadInput @style/Component.Status.MessageThread + + + + + + + + + + + + + + + +