Java tutorial
/** * Wire * Copyright (C) 2016 Wire Swiss GmbH * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.waz.zclient; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.res.Configuration; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.drawable.ColorDrawable; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.support.annotation.NonNull; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.text.TextUtils; import android.view.Window; import android.view.WindowManager; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GooglePlayServicesUtil; import com.localytics.android.Localytics; import com.waz.api.ActiveVoiceChannels; import com.waz.api.CommonConnections; import com.waz.api.ConversationsList; import com.waz.api.IConversation; import com.waz.api.MessagesList; import com.waz.api.NetworkMode; import com.waz.api.Self; import com.waz.api.SyncState; import com.waz.api.User; import com.waz.api.Verification; import com.waz.api.VoiceChannel; import com.waz.model.ConvId; import com.waz.threading.Threading; import com.waz.zclient.calling.CallingActivity; import com.waz.zclient.calling.controllers.CallPermissionsController; import com.waz.zclient.controllers.accentcolor.AccentColorChangeRequester; import com.waz.zclient.controllers.accentcolor.AccentColorObserver; import com.waz.zclient.controllers.calling.CallingObserver; import com.waz.zclient.controllers.navigation.NavigationControllerObserver; import com.waz.zclient.controllers.navigation.Page; import com.waz.zclient.controllers.tracking.events.connect.AcceptedGenericInviteEvent; import com.waz.zclient.controllers.tracking.events.exception.ExceptionEvent; import com.waz.zclient.controllers.tracking.events.otr.VerifiedConversationEvent; import com.waz.zclient.controllers.tracking.events.profile.SignOut; import com.waz.zclient.controllers.tracking.screens.ApplicationScreen; import com.waz.zclient.controllers.userpreferences.IUserPreferencesController; import com.waz.zclient.controllers.userpreferences.UserPreferencesController; import com.waz.zclient.core.api.scala.AppEntryStore; import com.waz.zclient.core.controllers.tracking.attributes.RangedAttribute; import com.waz.zclient.core.controllers.tracking.events.Event; import com.waz.zclient.core.controllers.tracking.events.media.OpenedMediaActionEvent; import com.waz.zclient.core.controllers.tracking.events.session.LoggedOutEvent; import com.waz.zclient.core.stores.api.ZMessagingApiStoreObserver; import com.waz.zclient.core.stores.connect.ConnectStoreObserver; import com.waz.zclient.core.stores.connect.IConnectStore; import com.waz.zclient.core.stores.conversation.ConversationChangeRequester; import com.waz.zclient.core.stores.conversation.ConversationStoreObserver; import com.waz.zclient.core.stores.network.NetworkAction; import com.waz.zclient.core.stores.profile.ProfileStoreObserver; import com.waz.zclient.pages.main.MainPhoneFragment; import com.waz.zclient.pages.main.MainTabletFragment; import com.waz.zclient.pages.main.connectivity.ConnectivityFragment; import com.waz.zclient.pages.main.grid.GridFragment; import com.waz.zclient.pages.main.profile.ZetaPreferencesActivity; import com.waz.zclient.pages.startup.UpdateFragment; import com.waz.zclient.utils.BuildConfigUtils; import com.waz.zclient.utils.Emojis; import com.waz.zclient.utils.HockeyCrashReporting; import com.waz.zclient.utils.IntentUtils; import com.waz.zclient.utils.LayoutSpec; import com.waz.zclient.utils.PhoneUtils; import com.waz.zclient.utils.PhoneUtils.PhoneState; import com.waz.zclient.utils.StringUtils; import com.waz.zclient.utils.ViewUtils; import net.hockeyapp.android.NativeCrashManager; import timber.log.Timber; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Locale; public class MainActivity extends BaseActivity implements MainPhoneFragment.Container, MainTabletFragment.Container, GridFragment.Container, ConnectivityFragment.Container, UpdateFragment.Container, ProfileStoreObserver, AccentColorObserver, ConnectStoreObserver, NavigationControllerObserver, CallingObserver, OtrDeviceLimitFragment.Container, ZMessagingApiStoreObserver, ConversationStoreObserver { // Tags public static final String TAG = MainActivity.class.getName(); public static final int REQUEST_CODE_GOOGLE_PLAY_SERVICES_DIALOG = 56571; private static final int LAUNCH_CONVERSATION_CHANGE_DELAY = 123; /////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Lifecycle // /////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Sets to RGBA_8888 to ensure fluent gradients. */ @Override public void onAttachedToWindow() { super.onAttachedToWindow(); Window window = getWindow(); window.setFormat(PixelFormat.RGBA_8888); } @Override @SuppressWarnings("PMD") public void onCreate(final Bundle savedInstanceState) { Timber.i("onCreate"); if (getActionBar() != null) { getActionBar().hide(); } super.onCreate(savedInstanceState); //Prevent drawing the default background to reduce overdraw getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); setContentView(R.layout.main); if (LayoutSpec.isPhone(this)) { ViewUtils.lockScreenOrientation(Configuration.ORIENTATION_PORTRAIT, this); } FragmentManager fragmentManager = getSupportFragmentManager(); getStoreFactory().getZMessagingApiStore().getApi().getMediaManager(); initializeControllers(); if (!getControllerFactory().getUserPreferencesController().showStatusBar()) { getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); } if (savedInstanceState == null) { FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); fragmentTransaction.add(R.id.fl__offline__container, ConnectivityFragment.newInstance(), ConnectivityFragment.TAG); if (BuildConfig.SHOW_GRIDOVERLAY) { fragmentTransaction.add(R.id.fl_main_grid, GridFragment.newInstance(), GridFragment.TAG); } fragmentTransaction.commit(); } else { getControllerFactory().getNavigationController().onActivityCreated(savedInstanceState); } if (BuildConfigUtils.isHockeyUpdateEnabled() && !BuildConfigUtils.isLocalBuild(this)) { HockeyCrashReporting.checkForUpdates(this); } getControllerFactory().getTrackingController().appLaunched(getIntent()); String appCrash = getControllerFactory().getUserPreferencesController().getCrashException(); String appCrashDetails = getControllerFactory().getUserPreferencesController().getCrashDetails(); if (appCrash != null) { Event exceptionEvent = ExceptionEvent.exception(appCrash, appCrashDetails); getControllerFactory().getTrackingController().tagEvent(exceptionEvent); } getControllerFactory().getLoadTimeLoggerController().appStart(); } @Override protected void onResumeFragments() { Timber.i("onResumeFragments"); super.onResumeFragments(); getStoreFactory().getZMessagingApiStore().addApiObserver(this); } @Override public void onStart() { getControllerFactory().getBackgroundController() .setSelf(getStoreFactory().getZMessagingApiStore().getApi().getSelf()); Timber.i("onStart"); overridePendingTransition(R.anim.fade_in, R.anim.fade_out); getStoreFactory().getProfileStore().addProfileStoreObserver(this); getStoreFactory().getConnectStore().addConnectRequestObserver(this); getControllerFactory().getAccentColorController().addAccentColorObserver(this); getControllerFactory().getNavigationController().addNavigationControllerObserver(this); getControllerFactory().getCallingController().addCallingObserver(this); getStoreFactory().getConversationStore().addConversationStoreObserver(this); handleInvite(); handleReferral(); super.onStart(); //This is needed to drag the user back to the calling activity if they open the app again during a call CallingActivity.startIfCallIsActive(this); if (!getControllerFactory().getUserPreferencesController().hasCheckedForUnsupportedEmojis(Emojis.VERSION)) { Threading.Background().execute(new Runnable() { @Override public void run() { checkForUnsupportedEmojis(); } }); } } @Override protected void onResume() { Timber.i("onResume"); super.onResume(); verifyGooglePlayServicesStatus(); final boolean trackingEnabled = getSharedPreferences(UserPreferencesController.USER_PREFS_TAG, Context.MODE_PRIVATE).getBoolean(getString(R.string.pref_advanced_analytics_enabled_key), true); if (trackingEnabled) { HockeyCrashReporting.checkForCrashes(getApplicationContext(), getControllerFactory().getUserPreferencesController().getDeviceId(), getControllerFactory().getTrackingController()); } else { HockeyCrashReporting.deleteCrashReports(getApplicationContext()); NativeCrashManager.deleteDumpFiles(getApplicationContext()); } getControllerFactory().getTrackingController().appResumed(); Localytics.setInAppMessageDisplayActivity(this); Localytics.handleTestMode(getIntent()); if (getControllerFactory().getThemeController().isRestartPending()) { getControllerFactory().getThemeController().removePendingRestart(); restartActivity(); } } private void restartActivity() { finish(); startActivity(IntentUtils.getAppLaunchIntent(this)); overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); } @Override protected void onPostResume() { super.onPostResume(); getControllerFactory().getNavigationController().markActivityResumed(); } @Override protected void onPause() { Timber.i("onPause"); Localytics.dismissCurrentInAppMessage(); Localytics.clearInAppMessageDisplayActivity(); getControllerFactory().getNavigationController().markActivityPaused(); getControllerFactory().getTrackingController().appPaused(); super.onPause(); } @Override protected void onSaveInstanceState(Bundle outState) { Timber.i("onSaveInstanceState"); getControllerFactory().getNavigationController().onSaveInstanceState(outState); super.onSaveInstanceState(outState); } @Override public void onStop() { Timber.i("onStop"); super.onStop(); getStoreFactory().getConversationStore().removeConversationStoreObserver(this); getControllerFactory().getCallingController().removeCallingObserver(this); getStoreFactory().getZMessagingApiStore().removeApiObserver(this); getControllerFactory().getAccentColorController().removeAccentColorObserver(this); getStoreFactory().getConnectStore().removeConnectRequestObserver(this); getStoreFactory().getProfileStore().removeProfileStoreObserver(this); getControllerFactory().getNavigationController().removeNavigationControllerObserver(this); getControllerFactory().getUserPreferencesController() .setLastAccentColor(getStoreFactory().getProfileStore().getAccentColor()); getControllerFactory().getBackgroundController().onStop(); } @Override public void onBackPressed() { Timber.i("onBackPressed"); Fragment currentFragment; currentFragment = getSupportFragmentManager().findFragmentById(R.id.fl__calling__container); if (currentFragment instanceof OnBackPressedListener) { boolean consumed = ((OnBackPressedListener) currentFragment).onBackPressed(); if (consumed) { return; } } currentFragment = getSupportFragmentManager().findFragmentById(R.id.fl_main_content); if (currentFragment instanceof OnBackPressedListener) { boolean consumed = ((OnBackPressedListener) currentFragment).onBackPressed(); if (consumed) { return; } } currentFragment = getSupportFragmentManager().findFragmentById(R.id.fl_main_otr_warning); if (currentFragment instanceof OnBackPressedListener) { boolean consumed = ((OnBackPressedListener) currentFragment).onBackPressed(); if (consumed) { return; } } super.onBackPressed(); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { Timber.i("OnActivity result: %d/%d", requestCode, resultCode); super.onActivityResult(requestCode, resultCode, data); getSupportFragmentManager().findFragmentById(R.id.fl_main_content).onActivityResult(requestCode, resultCode, data); getControllerFactory().getSpotifyController().handleActivityResult(requestCode, resultCode, data); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); if (IntentUtils.isPasswordResetIntent(intent)) { onPasswordWasReset(); } getControllerFactory().getTrackingController().appLaunched(intent); String appCrash = getControllerFactory().getUserPreferencesController().getCrashException(); String appCrashDetails = getControllerFactory().getUserPreferencesController().getCrashDetails(); if (appCrash != null) { Event exceptionEvent = ExceptionEvent.exception(appCrash, appCrashDetails); getControllerFactory().getTrackingController().tagEvent(exceptionEvent); } setIntent(intent); String page = intent.getStringExtra(LaunchActivity.APP_PAGE); if (page == null || TextUtils.isEmpty(page)) { return; } setIntent(IntentUtils.resetAppPage(intent)); switch (page) { case IntentUtils.LOCALYTICS_DEEPLINK_SEARCH: case IntentUtils.LOCALYTICS_DEEPLINK_PROFILE: restartAppWithPage(page); break; case IntentUtils.LOCALYTICS_DEEPLINK_SETTINGS: startActivity(ZetaPreferencesActivity.getDefaultIntent(this)); break; } } private void restartAppWithPage(String page) { Intent intent = new Intent(this, MainActivity.class); intent.putExtra(LaunchActivity.APP_PAGE, page); startActivity(intent); finish(); } private void initializeControllers() { // Make sure we have a running OrientationController instance getControllerFactory().getOrientationController(); // Here comes code for adding other dependencies to controllers... getControllerFactory().getNavigationController().setIsLandscape(ViewUtils.isInLandscape(this)); } private void verifyGooglePlayServicesStatus() { int deviceGooglePlayServicesState = GooglePlayServicesUtil .isGooglePlayServicesAvailable(getApplicationContext()); if (deviceGooglePlayServicesState == ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED) { GooglePlayServicesUtil .getErrorDialog(deviceGooglePlayServicesState, this, REQUEST_CODE_GOOGLE_PLAY_SERVICES_DIALOG) .show(); } } /////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Navigation // /////////////////////////////////////////////////////////////////////////////////////////////////////////// private void enterApplication(final Self self) { Timber.e("Entering application"); // step 1 - check if app was started via password reset intent if (IntentUtils.isPasswordResetIntent(getIntent())) { Timber.e("Password was reset"); onPasswordWasReset(); return; } // step 2 - no one is logged in if (!self.isLoggedIn()) { // finally - no one is logged in Timber.e("No user is logged in"); openSignUpPage(); return; } switch (self.getClientRegistrationState()) { case PASSWORD_MISSING: if (!TextUtils.isEmpty(self.getEmail())) { startActivity(new Intent(this, OTRSignInActivity.class)); finish(); return; } else { getStoreFactory().getZMessagingApiStore().getApi().logout(); } break; case LIMIT_REACHED: showUnableToRegisterOtrClientDialog(); break; default: } onUserLoggedInAndVerified(self); } private void onPasswordWasReset() { getStoreFactory().getZMessagingApiStore().getApi().logout(); openSignUpPage(); } private void onUserLoggedInAndVerified(Self self) { getStoreFactory().getProfileStore().setUser(self); getControllerFactory().getAccentColorController().setColor(AccentColorChangeRequester.LOGIN, self.getAccent().getColor()); final Intent intent = getIntent(); if (IntentUtils.isLaunchFromNotificationIntent(intent)) { final IConversation conversation = getStoreFactory().getConversationStore() .getConversation(IntentUtils.getLaunchConversationId(intent)); final boolean startCallNotificationIntent = IntentUtils.isStartCallNotificationIntent(intent); Timber.i("Start from notification with call=%b", startCallNotificationIntent); IntentUtils.clearLaunchIntentExtra(intent); if (conversation != null) { // Only want to swipe over when app has loaded new Handler().postDelayed(new Runnable() { @Override public void run() { getStoreFactory().getConversationStore().setCurrentConversation(conversation, ConversationChangeRequester.NOTIFICATION); if (startCallNotificationIntent) { startCall(false); } } }, LAUNCH_CONVERSATION_CHANGE_DELAY); } } else if (IntentUtils.isLaunchFromSharingIntent(intent)) { final IConversation conversation = getStoreFactory().getConversationStore() .getConversation(IntentUtils.getLaunchConversationId(intent)); if (conversation != null) { String sharedText = IntentUtils.getLaunchConversationSharedText(intent); List<Uri> sharedFileUris = IntentUtils.getLaunchConversationSharedFiles(intent); getControllerFactory().getSharingController().setSharedText(sharedText); getControllerFactory().getSharingController().setSharedUris(sharedFileUris); getControllerFactory().getSharingController().setSharingConversationId(conversation.getId()); // Only want to swipe over when app has loaded new Handler().postDelayed(new Runnable() { @Override public void run() { getStoreFactory().getConversationStore().setCurrentConversation(conversation, ConversationChangeRequester.SHARING); } }, LAUNCH_CONVERSATION_CHANGE_DELAY); } IntentUtils.clearLaunchIntentExtra(intent); } setIntent(intent); openMainPage(); } /** * Depending on the orientation it opens either * MainPhoneFragment or MainTabletFragment. At the * beginning it checks if it is already setup properly. */ private void openMainPage() { if (LayoutSpec.isPhone(this)) { if (getSupportFragmentManager().findFragmentByTag(MainPhoneFragment.TAG) == null) { replaceMainFragment(new MainPhoneFragment(), MainPhoneFragment.TAG); } Timber.i("No need to open main fragment"); } else { if (getSupportFragmentManager().findFragmentByTag(MainTabletFragment.TAG) == null) { replaceMainFragment(new MainTabletFragment(), MainTabletFragment.TAG); } Timber.i("No need to open main fragment"); } } private void openSignUpPage() { Intent intent = new Intent(getApplicationContext(), AppEntryActivity.class); startActivity(intent); finish(); } private void openForceUpdatePage() { Intent intent = new Intent(getApplicationContext(), ForceUpdateActivity.class); startActivity(intent); finish(); } private void replaceMainFragment(Fragment fragment, String TAG) { getSupportFragmentManager().beginTransaction().replace(R.id.fl_main_content, fragment, TAG).commit(); } private void handleInvite() { String token = getControllerFactory().getUserPreferencesController().getGenericInvitationToken(); getControllerFactory().getUserPreferencesController().setGenericInvitationToken(null); if (TextUtils.isEmpty(token) || TextUtils.equals(token, AppEntryStore.GENERAL_GENERIC_INVITE_TOKEN)) { return; } getStoreFactory().getConnectStore().requestConnection(token); getControllerFactory().getTrackingController().tagEvent(new AcceptedGenericInviteEvent()); } private void handleReferral() { String referralToken = getControllerFactory().getUserPreferencesController().getReferralToken(); getControllerFactory().getUserPreferencesController().setReferralToken(null); if (TextUtils.isEmpty(referralToken) || TextUtils.equals(referralToken, AppEntryStore.GENERAL_GENERIC_INVITE_TOKEN)) { return; } getStoreFactory().getConnectStore().requestConnection(referralToken); getControllerFactory().getTrackingController().tagEvent(new AcceptedGenericInviteEvent()); } ////////////////////////////////////////////////////////////////////////////////////////// // // Notifications // ////////////////////////////////////////////////////////////////////////////////////////// @Override public void onLogout() { Timber.i("onLogout"); getStoreFactory().reset(); getControllerFactory().getPickUserController().hideUserProfile(); getControllerFactory().getUserPreferencesController().reset(); getControllerFactory().getSpotifyController().logout(); getStoreFactory().getConversationStore().onLogout(); getControllerFactory().getNavigationController().resetPagerPositionToDefault(); Intent intent = new Intent(this, MainActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); finish(); startActivity(intent); } @Override public void onForceClientUpdate() { openForceUpdatePage(); } @Override public void onAccentColorHasChanged(Object sender, int color) { getControllerFactory().getUserPreferencesController().setLastAccentColor(color); } ////////////////////////////////////////////////////////////////////////////////////////// // // GroupCallingStoreObserver // ////////////////////////////////////////////////////////////////////////////////////////// @Override public void onAccentColorChangedRemotely(Object sender, int color) { getControllerFactory().getAccentColorController().setColor(AccentColorChangeRequester.REMOTE, color); } @Override public void onMyNameHasChanged(Object sender, String myName) { } @Override public void onMyEmailHasChanged(String myEmail, boolean isVerified) { } @Override public void onMyPhoneHasChanged(String myPhone, boolean isVerified) { } @Override public void onPhoneUpdateFailed(String myPhone, int errorCode, String message, String label) { } @Override public void onMyEmailAndPasswordHasChanged(String myEmail) { } ////////////////////////////////////////////////////////////////////////////////////////// // // NavigationControllerObserver // ////////////////////////////////////////////////////////////////////////////////////////// @Override public void onPageVisible(Page page) { getControllerFactory().getGlobalLayoutController().setSoftInputModeForPage(page); getControllerFactory().getNavigationController().setPagerSettingForPage(page); switch (page) { case CONVERSATION_LIST: getControllerFactory().getTrackingController().onApplicationScreen(ApplicationScreen.CONVERSATION_LIST); break; case MESSAGE_STREAM: IConversation currentConversation = getStoreFactory().getConversationStore().getCurrentConversation(); if (currentConversation == null) { break; } String conversationName = currentConversation.getName(); if (currentConversation.getType() == IConversation.Type.ONE_TO_ONE && ((conversationName.toLowerCase(Locale.getDefault()).contains("otto") && conversationName.toLowerCase(Locale.getDefault()).contains("bot")) || TextUtils.equals(currentConversation.getOtherParticipant().getEmail(), "ottobot@wire.com"))) { getControllerFactory().getTrackingController() .onApplicationScreen(ApplicationScreen.CONVERSATION__BOT); } else { getControllerFactory().getTrackingController().onApplicationScreen(ApplicationScreen.CONVERSATION); } break; case CAMERA: getControllerFactory().getTrackingController().onApplicationScreen(ApplicationScreen.CAMERA); break; case DRAWING: getControllerFactory().getTrackingController().onApplicationScreen(ApplicationScreen.DRAW_SKETCH); break; case CONNECT_REQUEST_INBOX: getControllerFactory().getTrackingController() .onApplicationScreen(ApplicationScreen.CONVERSATION__INBOX); break; case PENDING_CONNECT_REQUEST_AS_CONVERSATION: getControllerFactory().getTrackingController() .onApplicationScreen(ApplicationScreen.CONVERSATION__PENDING); break; case PARTICIPANT: getControllerFactory().getTrackingController() .onApplicationScreen(ApplicationScreen.CONVERSATION__PARTICIPANTS); break; case PICK_USER_ADD_TO_CONVERSATION: getControllerFactory().getTrackingController() .onApplicationScreen(ApplicationScreen.START_UI__ADD_TO_CONVERSATION); break; case PICK_USER: getControllerFactory().getTrackingController().onApplicationScreen(ApplicationScreen.START_UI); break; } } @Override public void onPageStateHasChanged(Page page) { } ////////////////////////////////////////////////////////////////////////////////////////// // // ConnectStoreObserver // ////////////////////////////////////////////////////////////////////////////////////////// @Override public void onMessagesUpdated(MessagesList messagesList) { } @Override public void onConnectUserUpdated(User user, IConnectStore.UserRequester usertype) { } @Override public void onCommonConnectionsUpdated(CommonConnections commonConnections) { } @Override public void onInviteRequestSent(IConversation conversation) { getStoreFactory().getConversationStore().setCurrentConversation(conversation, ConversationChangeRequester.INVITE); } ////////////////////////////////////////////////////////////////////////////////////////// // // Browser // ////////////////////////////////////////////////////////////////////////////////////////// @Override public void onOpenUrl(String url) { try { if (!url.startsWith("http://") && !url.startsWith("https://")) { url = "http://" + url; } Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(browserIntent); } catch (Exception e) { Timber.e("Failed to open URL: %s", url); } } private void showUnableToRegisterOtrClientDialog() { Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fl_main_otr_warning); if (fragment != null) { return; } getSupportFragmentManager().beginTransaction() .replace(R.id.fl_main_otr_warning, OtrDeviceLimitFragment.newInstance(), OtrDeviceLimitFragment.TAG) .addToBackStack(OtrDeviceLimitFragment.TAG).commitAllowingStateLoss(); } @Override public void logout() { getSupportFragmentManager().popBackStackImmediate(); // TODO: Remove old SignOut event AN-4232 getControllerFactory().getTrackingController().tagEvent(new SignOut()); getControllerFactory().getTrackingController().tagEvent(new LoggedOutEvent()); getStoreFactory().getZMessagingApiStore().logout(); } @Override public void manageDevices() { getSupportFragmentManager().popBackStackImmediate(); startActivity(ZetaPreferencesActivity.getOtrDevicesPreferencesIntent(this)); } @Override public void dismissOtrDeviceLimitFragment() { getSupportFragmentManager().popBackStackImmediate(); } @Override public void onInitialized(Self self) { enterApplication(self); } ////////////////////////////////////////////////////////////////////////////////////////// // // Cursor callback // ////////////////////////////////////////////////////////////////////////////////////////// @Override public void onStartCall(boolean withVideo) { handleOnStartCall(withVideo); boolean isGroupConversation = getStoreFactory().getConversationStore().getCurrentConversation() .getType() == IConversation.Type.GROUP; if (withVideo) { getControllerFactory().getTrackingController() .tagEvent(OpenedMediaActionEvent.videocall(isGroupConversation)); } else { getControllerFactory().getTrackingController() .tagEvent(OpenedMediaActionEvent.audiocall(isGroupConversation)); } } private void handleOnStartCall(final boolean withVideo) { if (PhoneUtils.getPhoneState(this) == PhoneState.IDLE && getActiveVoiceChannels().hasOngoingCall()) { cannotStartAlreadyHaveVoiceActive(withVideo); } else if (PhoneUtils.getPhoneState(this) != PhoneState.IDLE) { cannotStartGSM(); } else { startCallIfInternet(withVideo); } } private ActiveVoiceChannels getActiveVoiceChannels() { return getStoreFactory().getZMessagingApiStore().getApi().getActiveVoiceChannels(); } private void startCall(boolean withVideo) { final IConversation currentConversation = getStoreFactory().getConversationStore().getCurrentConversation(); if (currentConversation == null) { return; } final VoiceChannel voiceChannel = currentConversation.getVoiceChannel(); if (currentConversation.hasUnjoinedCall() && voiceChannel.isVideoCall() != withVideo) { ViewUtils.showAlertDialog(this, getString(R.string.calling__cannot_start__ongoing_different_kind__title, currentConversation.getName()), getString(R.string.calling__cannot_start__ongoing_different_kind__message), getString(R.string.calling__cannot_start__button), null, true); return; } injectJava(CallPermissionsController.class).startCall(new ConvId(voiceChannel.getConversation().getId()), withVideo); } ////////////////////////////////////////////////////////////////////////////////////////// // // VoiceChannel.JoinCallback // ////////////////////////////////////////////////////////////////////////////////////////// private void cannotStartAlreadyHaveVoiceActive(final boolean withVideo) { ViewUtils.showAlertDialog(this, R.string.calling__cannot_start__ongoing_voice__title, R.string.calling__cannot_start__ongoing_voice__message, R.string.calling__cannot_start__ongoing_voice__button_positive, R.string.calling__cannot_start__ongoing_voice__button_negative, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (getStoreFactory() == null || getStoreFactory().isTornDown()) { return; } final ActiveVoiceChannels activeVoiceChannels = getActiveVoiceChannels(); if (activeVoiceChannels.getOngoingCall() == null) { return; } activeVoiceChannels.getOngoingCall().leave(); new Handler().postDelayed(new Runnable() { @Override public void run() { if (getStoreFactory() == null || getStoreFactory().isTornDown()) { return; } startCall(withVideo); getControllerFactory().getTrackingController() .updateSessionAggregates(RangedAttribute.VOICE_CALLS_INITIATED); } }, getResources().getInteger(R.integer.calling__new_outgoing_call__delay_after_hangup)); } }, null); } private void cannotStartGSM() { ViewUtils.showAlertDialog(this, R.string.calling__cannot_start__title, R.string.calling__cannot_start__message, R.string.calling__cannot_start__button, null, true); } private void startCallIfInternet(final boolean withVideo) { getStoreFactory().getNetworkStore().doIfHasInternetOrNotifyUser(new NetworkAction() { @Override public void execute(NetworkMode networkMode) { switch (networkMode) { case _2G: ViewUtils.showAlertDialog(MainActivity.this, R.string.calling__slow_connection__title, R.string.calling__slow_connection__message, R.string.calling__slow_connection__button, null, true); break; case EDGE: if (withVideo) { ViewUtils.showAlertDialog(MainActivity.this, R.string.calling__slow_connection__title, R.string.calling__video_call__slow_connection__message, R.string.calling__slow_connection__button, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { startCall(true); } }, true); break; } case _3G: case _4G: case WIFI: startCall(withVideo); break; } } @Override public void onNoNetwork() { ViewUtils.showAlertDialog(MainActivity.this, R.string.alert_dialog__no_network__header, R.string.calling__call_drop__message, R.string.alert_dialog__confirmation, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }, false); } }); } @Override public void onConversationListUpdated(@NonNull ConversationsList conversationsList) { } @Override public void onConversationListStateHasChanged(ConversationsList.ConversationsListState state) { } @Override public void onCurrentConversationHasChanged(IConversation fromConversation, IConversation toConversation, ConversationChangeRequester conversationChangerSender) { } @Override public void onConversationSyncingStateHasChanged(SyncState syncState) { } @Override public void onMenuConversationHasChanged(IConversation fromConversation) { } @Override public void onVerificationStateChanged(String conversationId, Verification previousVerification, Verification currentVerification) { if (getControllerFactory() == null || getControllerFactory().isTornDown()) { return; } if (previousVerification != Verification.VERIFIED && currentVerification == Verification.VERIFIED) { getControllerFactory().getTrackingController().tagEvent(new VerifiedConversationEvent()); } } private void checkForUnsupportedEmojis() { if (getControllerFactory() == null || getControllerFactory().isTornDown()) { return; } IUserPreferencesController userPreferencesController = getControllerFactory() .getUserPreferencesController(); Collection<String> unsupported = new ArrayList<>(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { Paint paint = new Paint(); for (String[] array : Emojis.getAllEmojisSortedByCategory()) { for (String emoji : array) { if (!paint.hasGlyph(emoji)) { unsupported.add(emoji); } } } } else { for (String[] array : Emojis.getAllEmojisSortedByCategory()) { unsupported.addAll(Arrays.asList(array)); } unsupported = StringUtils.getMissingInFont(unsupported); } if (!unsupported.isEmpty()) { userPreferencesController.setUnsupportedEmoji(unsupported, Emojis.VERSION); } } }