| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582 |
- /* _____ _
- * |_ _| |_ _ _ ___ ___ _ __ __ _
- * | | | ' \| '_/ -_) -_) ' \/ _` |_
- * |_| |_||_|_| \___\___|_|_|_\__,_(_)
- *
- * Threema for Android
- * Copyright (c) 2013-2022 Threema GmbH
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
- package ch.threema.app.fragments;
- import android.Manifest;
- import android.annotation.SuppressLint;
- import android.app.Activity;
- import android.content.ClipData;
- import android.content.ClipboardManager;
- import android.content.Context;
- import android.content.Intent;
- import android.content.pm.PackageManager;
- import android.content.res.AssetFileDescriptor;
- import android.content.res.Configuration;
- import android.graphics.Bitmap;
- import android.graphics.Paint;
- import android.media.AudioAttributes;
- import android.media.AudioManager;
- import android.media.MediaPlayer;
- import android.net.Uri;
- import android.os.AsyncTask;
- import android.os.Build;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.SystemClock;
- import android.os.Vibrator;
- import android.text.Editable;
- import android.text.TextUtils;
- import android.text.TextWatcher;
- import android.text.format.DateUtils;
- import android.util.Pair;
- import android.view.Gravity;
- import android.view.HapticFeedbackConstants;
- import android.view.KeyEvent;
- import android.view.LayoutInflater;
- import android.view.Menu;
- import android.view.MenuInflater;
- import android.view.MenuItem;
- import android.view.MotionEvent;
- import android.view.View;
- import android.view.ViewGroup;
- import android.view.ViewStub;
- import android.view.WindowManager;
- import android.view.animation.LinearInterpolator;
- import android.view.inputmethod.EditorInfo;
- import android.widget.AbsListView;
- import android.widget.Filter;
- import android.widget.FrameLayout;
- import android.widget.ImageButton;
- import android.widget.ImageView;
- import android.widget.LinearLayout;
- import android.widget.ProgressBar;
- import android.widget.RelativeLayout;
- import android.widget.ScrollView;
- import android.widget.TextView;
- import android.widget.Toast;
- import com.google.android.material.snackbar.Snackbar;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import java.io.File;
- import java.sql.SQLException;
- import java.text.SimpleDateFormat;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Collections;
- import java.util.Comparator;
- import java.util.Date;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import java.util.concurrent.CopyOnWriteArrayList;
- import java.util.regex.Pattern;
- import androidx.annotation.AnyThread;
- import androidx.annotation.ColorInt;
- import androidx.annotation.NonNull;
- import androidx.annotation.Nullable;
- import androidx.annotation.UiThread;
- import androidx.annotation.WorkerThread;
- import androidx.appcompat.app.ActionBar;
- import androidx.appcompat.app.AppCompatActivity;
- import androidx.appcompat.view.ActionMode;
- import androidx.appcompat.widget.SearchView;
- import androidx.coordinatorlayout.widget.CoordinatorLayout;
- import androidx.core.content.ContextCompat;
- import androidx.core.content.pm.ShortcutManagerCompat;
- import androidx.core.graphics.ColorUtils;
- import androidx.core.view.MenuItemCompat;
- import androidx.core.view.OnApplyWindowInsetsListener;
- import androidx.core.view.ViewCompat;
- import androidx.core.view.WindowInsetsCompat;
- import androidx.fragment.app.Fragment;
- import androidx.fragment.app.FragmentActivity;
- import androidx.lifecycle.DefaultLifecycleObserver;
- import androidx.lifecycle.LifecycleOwner;
- import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
- import androidx.transition.Slide;
- import androidx.transition.Transition;
- import androidx.transition.TransitionManager;
- import ch.threema.app.R;
- import ch.threema.app.ThreemaApplication;
- import ch.threema.app.actions.SendAction;
- import ch.threema.app.actions.TextMessageSendAction;
- import ch.threema.app.activities.ComposeMessageActivity;
- import ch.threema.app.activities.ContactDetailActivity;
- import ch.threema.app.activities.ContactNotificationsActivity;
- import ch.threema.app.activities.DistributionListAddActivity;
- import ch.threema.app.activities.GroupNotificationsActivity;
- import ch.threema.app.activities.HomeActivity;
- import ch.threema.app.activities.MediaGalleryActivity;
- import ch.threema.app.activities.RecipientListBaseActivity;
- import ch.threema.app.activities.SendMediaActivity;
- import ch.threema.app.activities.TextChatBubbleActivity;
- import ch.threema.app.activities.ThreemaActivity;
- import ch.threema.app.activities.ThreemaToolbarActivity;
- import ch.threema.app.activities.WorkExplainActivity;
- import ch.threema.app.activities.ballot.BallotOverviewActivity;
- import ch.threema.app.adapters.ComposeMessageAdapter;
- import ch.threema.app.adapters.decorators.ChatAdapterDecorator;
- import ch.threema.app.asynctasks.EmptyChatAsyncTask;
- import ch.threema.app.cache.ThumbnailCache;
- import ch.threema.app.dialogs.ExpandableTextEntryDialog;
- import ch.threema.app.dialogs.GenericAlertDialog;
- import ch.threema.app.dialogs.GenericProgressDialog;
- import ch.threema.app.dialogs.MessageDetailDialog;
- import ch.threema.app.dialogs.SelectorDialog;
- import ch.threema.app.emojis.EmojiButton;
- import ch.threema.app.emojis.EmojiMarkupUtil;
- import ch.threema.app.emojis.EmojiPicker;
- import ch.threema.app.emojis.EmojiTextView;
- import ch.threema.app.grouplinks.IncomingGroupRequestActivity;
- import ch.threema.app.grouplinks.OpenGroupRequestNoticeView;
- import ch.threema.app.listeners.BallotListener;
- import ch.threema.app.listeners.ContactListener;
- import ch.threema.app.listeners.ContactTypingListener;
- import ch.threema.app.listeners.ConversationListener;
- import ch.threema.app.listeners.GroupListener;
- import ch.threema.app.listeners.MessageListener;
- import ch.threema.app.listeners.MessagePlayerListener;
- import ch.threema.app.listeners.QRCodeScanListener;
- import ch.threema.app.managers.ListenerManager;
- import ch.threema.app.managers.ServiceManager;
- import ch.threema.app.mediaattacher.MediaAttachActivity;
- import ch.threema.app.mediaattacher.MediaFilterQuery;
- import ch.threema.app.messagereceiver.ContactMessageReceiver;
- import ch.threema.app.messagereceiver.MessageReceiver;
- import ch.threema.app.routines.ReadMessagesRoutine;
- import ch.threema.app.services.ContactService;
- import ch.threema.app.services.ConversationService;
- import ch.threema.app.services.DeadlineListService;
- import ch.threema.app.services.DeviceService;
- import ch.threema.app.services.DistributionListService;
- import ch.threema.app.services.DownloadService;
- import ch.threema.app.services.FileService;
- import ch.threema.app.services.GroupService;
- import ch.threema.app.services.IdListService;
- import ch.threema.app.services.MessageService;
- import ch.threema.app.services.NotificationService;
- import ch.threema.app.services.PreferenceService;
- import ch.threema.app.services.RingtoneService;
- import ch.threema.app.services.UserService;
- import ch.threema.app.services.WallpaperService;
- import ch.threema.app.services.ballot.BallotService;
- import ch.threema.app.services.license.LicenseService;
- import ch.threema.app.services.messageplayer.MessagePlayerService;
- import ch.threema.app.ui.AvatarView;
- import ch.threema.app.ui.ContentCommitComposeEditText;
- import ch.threema.app.ui.ConversationListView;
- import ch.threema.app.ui.DebouncedOnClickListener;
- import ch.threema.app.ui.ListViewSwipeListener;
- import ch.threema.app.ui.MentionSelectorPopup;
- import ch.threema.app.ui.OpenBallotNoticeView;
- import ch.threema.app.ui.QRCodePopup;
- import ch.threema.app.ui.SelectorDialogItem;
- import ch.threema.app.ui.SendButton;
- import ch.threema.app.ui.SingleToast;
- import ch.threema.app.ui.TooltipPopup;
- import ch.threema.app.ui.TypingIndicatorTextWatcher;
- import ch.threema.app.ui.VerificationLevelImageView;
- import ch.threema.app.ui.listitemholder.ComposeMessageHolder;
- import ch.threema.app.utils.AnimationUtil;
- import ch.threema.app.utils.AppRestrictionUtil;
- import ch.threema.app.utils.BallotUtil;
- import ch.threema.app.utils.ConfigUtils;
- import ch.threema.app.utils.ContactUtil;
- import ch.threema.app.utils.ConversationUtil;
- import ch.threema.app.utils.DialogUtil;
- import ch.threema.app.utils.EditTextUtil;
- import ch.threema.app.utils.FileUtil;
- import ch.threema.app.utils.IntentDataUtil;
- import ch.threema.app.utils.LinkifyUtil;
- import ch.threema.app.utils.LocaleUtil;
- import ch.threema.app.utils.LogUtil;
- import ch.threema.app.utils.MediaPlayerStateWrapper;
- import ch.threema.app.utils.MessageUtil;
- import ch.threema.app.utils.NameUtil;
- import ch.threema.app.utils.NavigationUtil;
- import ch.threema.app.utils.QuoteUtil;
- import ch.threema.app.utils.RuntimeUtil;
- import ch.threema.app.utils.ShortcutUtil;
- import ch.threema.app.utils.TestUtil;
- import ch.threema.app.utils.ToolbarUtil;
- import ch.threema.app.voicemessage.VoiceRecorderActivity;
- import ch.threema.app.voip.listeners.VoipCallEventListener;
- import ch.threema.app.voip.managers.VoipListenerManager;
- import ch.threema.app.voip.services.VoipStateService;
- import ch.threema.app.voip.util.VoipUtil;
- import ch.threema.domain.models.IdentityType;
- import ch.threema.domain.protocol.csp.messages.file.FileData;
- import ch.threema.storage.DatabaseServiceNew;
- import ch.threema.storage.models.AbstractMessageModel;
- import ch.threema.storage.models.ContactModel;
- import ch.threema.storage.models.ConversationModel;
- import ch.threema.storage.models.DateSeparatorMessageModel;
- import ch.threema.storage.models.DistributionListMessageModel;
- import ch.threema.storage.models.DistributionListModel;
- import ch.threema.storage.models.FirstUnreadMessageModel;
- import ch.threema.storage.models.GroupMessageModel;
- import ch.threema.storage.models.GroupModel;
- import ch.threema.storage.models.MessageState;
- import ch.threema.storage.models.MessageType;
- import ch.threema.storage.models.ballot.BallotModel;
- import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
- import static ch.threema.app.ThreemaApplication.getAppContext;
- import static ch.threema.app.services.messageplayer.MessagePlayer.SOURCE_AUDIORECORDER;
- import static ch.threema.app.services.messageplayer.MessagePlayer.SOURCE_LIFECYCLE;
- import static ch.threema.app.services.messageplayer.MessagePlayer.SOURCE_VOIP;
- import static ch.threema.app.utils.LinkifyUtil.DIALOG_TAG_CONFIRM_LINK;
- import static ch.threema.app.utils.ShortcutUtil.TYPE_CHAT;
- public class ComposeMessageFragment extends Fragment implements
- LifecycleOwner,
- DefaultLifecycleObserver,
- SwipeRefreshLayout.OnRefreshListener,
- GenericAlertDialog.DialogClickListener,
- ChatAdapterDecorator.ActionModeStatus,
- SelectorDialog.SelectorDialogClickListener,
- EmojiPicker.EmojiPickerListener,
- MentionSelectorPopup.MentionSelectorListener,
- OpenBallotNoticeView.VisibilityListener,
- ThreemaToolbarActivity.OnSoftKeyboardChangedListener,
- ExpandableTextEntryDialog.ExpandableTextEntryDialogClickListener {
- private static final Logger logger = LoggerFactory.getLogger(ComposeMessageFragment.class);
- private static final String CONFIRM_TAG_DELETE_DISTRIBUTION_LIST = "deleteDistributionList";
- public static final String DIALOG_TAG_CONFIRM_CALL = "dtcc";
- private static final String DIALOG_TAG_CHOOSE_SHORTCUT_TYPE = "st";
- private static final String DIALOG_TAG_EMPTY_CHAT = "ccc";
- private static final String DIALOG_TAG_CONFIRM_BLOCK = "block";
- private static final String DIALOG_TAG_DECRYPTING_MESSAGES = "dcr";
- private static final String DIALOG_TAG_SEARCHING = "src";
- private static final String DIALOG_TAG_LOADING_MESSAGES = "loadm";
- public static final String EXTRA_API_MESSAGE_ID = "apimsgid";
- public static final String EXTRA_SEARCH_QUERY = "searchQuery";
- public static final String EXTRA_LAST_MEDIA_SEARCH_QUERY = "searchMediaQuery";
- public static final String EXTRA_LAST_MEDIA_TYPE_QUERY = "searchMediaType";
- private static final int PERMISSION_REQUEST_SAVE_MESSAGE = 2;
- private static final int PERMISSION_REQUEST_ATTACH_VOICE_MESSAGE = 7;
- private static final int PERMISSION_REQUEST_ATTACH_CAMERA = 8;
- private static final int PERMISSION_REQUEST_ATTACH_CAMERA_VIDEO = 11;
- private static final int ACTIVITY_ID_VOICE_RECORDER = 9731;
- public static final long VIBRATION_MSEC = 300;
- private static final long MESSAGE_PAGE_SIZE = 100;
- public static final int SCROLLBUTTON_VIEW_TIMEOUT = 3000;
- private static final int SMOOTHSCROLL_THRESHOLD = 10;
- private static final int MAX_SELECTED_ITEMS = 100; // may not be larger than MESSAGE_PAGE_SIZE
- private static final int MAX_FORWARDABLE_ITEMS = 50;
- private static final int CONTEXT_MENU_BOLD = 700;
- private static final int CONTEXT_MENU_ITALIC = 701;
- private static final int CONTEXT_MENU_STRIKETHRU = 702;
- private static final int CONTEXT_MENU_GROUP = 22100;
- private static final String CAMERA_URI = "camera_uri";
- private ContentCommitComposeEditText messageText;
- private SendButton sendButton;
- private ImageButton attachButton, cameraButton;
- private ContactModel contactModel;
- private MessageReceiver messageReceiver;
- private AudioManager audioManager;
- private ConversationListView convListView;
- private ComposeMessageAdapter composeMessageAdapter;
- private View isTypingView;
- private MenuItem mutedMenuItem = null, blockMenuItem = null, deleteDistributionListItem = null,
- callItem = null, shortCutItem = null, showOpenBallotWindowMenuItem = null,
- showBallotsMenuItem = null, showAllGroupRequestsMenuItem = null,
- showOpenGroupRequestsMenuItem = null;
- private TextView dateTextView;
- private ActionMode actionMode = null;
- private ActionMode searchActionMode = null;
- private ImageView quickscrollDownView = null, quickscrollUpView = null;
- private FrameLayout dateView = null;
- private FrameLayout bottomPanel = null;
- private String identity;
- private Integer groupId = 0, distributionListId = 0;
- private Uri cameraUri;
- private long intentTimestamp = 0L;
- private int longClickItem = AbsListView.INVALID_POSITION;
- private int listViewTop = 0, lastFirstVisibleItem = -1;
- private Snackbar deleteSnackbar;
- private TypingIndicatorTextWatcher typingIndicatorTextWatcher;
- private Map<String, Integer> identityColors;
- private MediaFilterQuery lastMediaFilter;
- private PreferenceService preferenceService;
- private ContactService contactService;
- private MessageService messageService;
- private NotificationService notificationService;
- private IdListService blackListIdentityService;
- private ConversationService conversationService;
- private DeviceService deviceService;
- private WallpaperService wallpaperService;
- private DeadlineListService mutedChatsListService, mentionOnlyChatsListService, hiddenChatsListService;
- private RingtoneService ringtoneService;
- private UserService userService;
- private FileService fileService;
- private VoipStateService voipStateService;
- private DownloadService downloadService;
- private LicenseService licenseService;
- private boolean listUpdateInProgress = false, isPaused = false;
- private final List<AbstractMessageModel> unreadMessages = new ArrayList<>();
- private final List<AbstractMessageModel> messageValues = new ArrayList<>();
- private final List<AbstractMessageModel> selectedMessages = new ArrayList<>(1);
- private final List<Pair<AbstractMessageModel, Integer>> deleteableMessages = new ArrayList<>(1);
- private EmojiMarkupUtil emojiMarkupUtil;
- private EmojiPicker emojiPicker;
- private EmojiButton emojiButton;
- private SwipeRefreshLayout swipeRefreshLayout;
- private Integer currentPageReferenceId = null;
- private EmojiTextView actionBarTitleTextView;
- private TextView actionBarSubtitleTextView;
- private VerificationLevelImageView actionBarSubtitleImageView;
- private AvatarView actionBarAvatarView;
- private ImageView wallpaperView;
- private ActionBar actionBar;
- private MentionSelectorPopup mentionPopup;
- private TooltipPopup workTooltipPopup;
- private OpenBallotNoticeView openBallotNoticeView;
- private OpenGroupRequestNoticeView openGroupRequestNoticeView;
- private ComposeMessageActivity activity;
- private View fragmentView;
- private CoordinatorLayout coordinatorLayout;
- private BallotService ballotService;
- private DatabaseServiceNew databaseServiceNew;
- private LayoutInflater layoutInflater;
- private ListViewSwipeListener listViewSwipeListener;
- private GroupService groupService;
- private boolean isGroupChat = false;
- private GroupModel groupModel;
- private Date listInitializedAt;
- private boolean isDistributionListChat = false;
- private DistributionListService distributionListService;
- private DistributionListModel distributionListModel;
- private MessagePlayerService messagePlayerService;
- private int listInstancePosition = AbsListView.INVALID_POSITION;
- private int listInstanceTop = 0;
- private String listInstanceReceiverId = null;
- private String conversationUid = null;
- private int unreadCount = 0;
- private final QuoteInfo quoteInfo = new QuoteInfo();
- private TextView searchCounter;
- private ProgressBar searchProgress;
- private ImageView searchNextButton, searchPreviousButton;
- @SuppressLint("SimpleDateFormat")
- private final SimpleDateFormat dayFormatter = new SimpleDateFormat("yyyyMMdd");
- private ThumbnailCache<?> thumbnailCache = null;
- private static class QuoteInfo {
- private FrameLayout quotePanel;
- private TextView quoteTextView, quoteIdentityView;
- private String quoteText, quoteIdentity;
- private View quoteBar;
- private ImageView quoteThumbnail;
- private ImageView quoteTypeImage;
- private AbstractMessageModel messageModel;
- }
- @Override
- public boolean getActionModeEnabled() {
- return actionMode != null;
- }
- private final MessageService.MessageFilter nextMessageFilter = new MessageService.MessageFilter() {
- @Override
- public long getPageSize() {
- return MESSAGE_PAGE_SIZE;
- }
- @Override
- public Integer getPageReferenceId() {
- return getCurrentPageReferenceId();
- }
- @Override
- public boolean withStatusMessages() {
- return true;
- }
- @Override
- public boolean withUnsaved() {
- return false;
- }
- @Override
- public boolean onlyUnread() {
- return false;
- }
- @Override
- public boolean onlyDownloaded() {
- return false;
- }
- @Override
- public MessageType[] types() {
- return null;
- }
- @Override
- public int[] contentTypes() {
- return null;
- }
- };
- // handler to remove dateview button after a certain time
- private final Handler dateViewHandler = new Handler();
- private final Runnable dateViewTask = () -> RuntimeUtil.runOnUiThread(() -> {
- if (dateView != null && dateView.getVisibility() == View.VISIBLE) {
- AnimationUtil.slideOutAnimation(dateView, false, 1f, null);
- AnimationUtil.setFadingVisibility(quickscrollUpView, View.GONE);
- AnimationUtil.setFadingVisibility(quickscrollDownView, View.GONE);
- }
- });
- // Listeners
- private final VoipCallEventListener voipCallEventListener = new VoipCallEventListener() {
- @Override
- public void onRinging(String peerIdentity) {
- }
- @Override
- public void onStarted(String peerIdentity, boolean outgoing) {
- logger.debug("VoipCallEventListener onStarted");
- updateVoipCallMenuItem(false);
- if (messagePlayerService != null) {
- messagePlayerService.pauseAll(SOURCE_VOIP);
- }
- }
- @Override
- public void onFinished(@NonNull String peerIdentity, boolean outgoing, int duration) {
- logger.debug("VoipCallEventListener onFinished");
- updateVoipCallMenuItem(true);
- }
- @Override
- public void onRejected(String peerIdentity, boolean outgoing, byte reason) {
- logger.debug("VoipCallEventListener onRejected");
- updateVoipCallMenuItem(true);
- }
- @Override
- public void onMissed(String peerIdentity, boolean accepted) {
- logger.debug("VoipCallEventListener onMissed");
- updateVoipCallMenuItem(true);
- }
- @Override
- public void onAborted(String peerIdentity) {
- logger.debug("VoipCallEventListener onAborted");
- updateVoipCallMenuItem(true); }
- };
- private final MessageListener messageListener = new MessageListener() {
- @Override
- public void onNew(final AbstractMessageModel newMessage) {
- if (newMessage != null) {
- RuntimeUtil.runOnUiThread(() -> {
- if (isAdded() && !isDetached() && !isRemoving()) {
- if (newMessage.isOutbox()) {
- if (addMessageToList(newMessage)) {
- if (!newMessage.isStatusMessage() && (newMessage.getType() != MessageType.VOIP_STATUS)) {
- playSentSound();
- }
- }
- } else {
- if (addMessageToList(newMessage) && !isPaused) {
- if (!newMessage.isStatusMessage() && (newMessage.getType() != MessageType.VOIP_STATUS)) {
- playReceivedSound();
- }
- }
- }
- }
- });
- }
- }
- @Override
- public void onModified(final List<AbstractMessageModel> modifiedMessageModels) {
- //replace model
- synchronized (messageValues) {
- for (final AbstractMessageModel modifiedMessageModel : modifiedMessageModels) {
- if (modifiedMessageModel.getId() != 0) {
- for (int n = 0; n < messageValues.size(); n++) {
- AbstractMessageModel listModel = messageValues.get(n);
- if (listModel != null && listModel.getId() == modifiedMessageModel.getId()) {
- //if the changed message is different to the created
- if (modifiedMessageModel != listModel) {
- //replace item
- messageValues.set(n, modifiedMessageModel);
- }
- break;
- }
- }
- }
- }
- }
- RuntimeUtil.runOnUiThread(() -> {
- if (composeMessageAdapter != null) {
- composeMessageAdapter.notifyDataSetChanged();
- }
- });
- }
- @Override
- public void onRemoved(final AbstractMessageModel removedMessageModel) {
- RuntimeUtil.runOnUiThread(() -> {
- if (TestUtil.required(composeMessageAdapter, removedMessageModel)) {
- composeMessageAdapter.remove(removedMessageModel);
- composeMessageAdapter.notifyDataSetChanged();
- }
- });
- }
- @Override
- public void onRemoved(List<AbstractMessageModel> removedMessageModels) {
- RuntimeUtil.runOnUiThread(() -> {
- if (TestUtil.required(composeMessageAdapter, removedMessageModels)) {
- for (AbstractMessageModel removedMessageModel : removedMessageModels) {
- composeMessageAdapter.remove(removedMessageModel);
- }
- RuntimeUtil.runOnUiThread(() -> {
- composeMessageAdapter.notifyDataSetChanged();
- });
- }
- });
- }
- @Override
- public void onProgressChanged(AbstractMessageModel messageModel, int newProgress) {
- //ignore
- RuntimeUtil.runOnUiThread(() -> {
- if (composeMessageAdapter != null) {
- composeMessageAdapter.notifyDataSetChanged();
- }
- });
- }
- };
- private final GroupListener groupListener = new GroupListener() {
- @Override
- public void onCreate(GroupModel newGroupModel) {
- //do nothing
- }
- @Override
- public void onRename(GroupModel groupModel) {
- updateToolBarTitleInUIThread();
- }
- @Override
- public void onUpdatePhoto(GroupModel groupModel) {
- updateToolBarTitleInUIThread();
- }
- @Override
- public void onRemove(GroupModel groupModel) {
- if (isGroupChat && groupId != null && groupId == groupModel.getId()) {
- RuntimeUtil.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if (activity != null) {
- activity.finish();
- }
- }
- });
- }
- }
- @Override
- public void onNewMember(GroupModel group, String newIdentity, int previousMemberCount) {
- updateToolBarTitleInUIThread();
- }
- @Override
- public void onMemberLeave(GroupModel group, String identity, int previousMemberCount) {
- updateToolBarTitleInUIThread();
- }
- @Override
- public void onMemberKicked(GroupModel group, String identity, int previousMemberCount) {
- updateToolBarTitleInUIThread();
- }
- @Override
- public void onUpdate(GroupModel groupModel) {
- updateToolBarTitleInUIThread();
- }
- @Override
- public void onLeave(GroupModel groupModel) {
- if (isGroupChat && groupId != null && groupId == groupModel.getId()) {
- RuntimeUtil.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if (activity != null) {
- activity.finish();
- }
- }
- });
- }
- }
- };
- private final ContactListener contactListener = new ContactListener() {
- @Override
- public void onModified(final ContactModel modifiedContactModel) {
- RuntimeUtil.runOnUiThread(() -> updateContactModelData(modifiedContactModel));
- }
- @Override
- public void onAvatarChanged(ContactModel contactModel) {
- updateToolBarTitleInUIThread();
- }
- @Override
- public void onRemoved(ContactModel removedContactModel) {
- if (contactModel != null && contactModel.equals(removedContactModel)) {
- // our contact has been removed. finish activity.
- RuntimeUtil.runOnUiThread(() -> {
- if (activity != null) {
- activity.finish();
- }
- });
- }
- }
- @Override
- public boolean handle(String handleIdentity) {
- //handle every contact change, affected contact models:
- //1. the contact of the chat
- //2. a contact as group member
- return true;
- }
- };
- private final ContactTypingListener contactTypingListener = new ContactTypingListener() {
- @Override
- public void onContactIsTyping(final ContactModel fromContact, final boolean isTyping) {
- RuntimeUtil.runOnUiThread(() -> {
- if (contactModel != null && fromContact.getIdentity().equals(contactModel.getIdentity())) {
- contactTypingStateChanged(isTyping);
- }
- });
- }
- };
- private final ConversationListener conversationListener = new ConversationListener() {
- @Override
- public void onNew(ConversationModel conversationModel) {}
- @Override
- public void onModified(ConversationModel modifiedConversationModel, Integer oldPosition) {}
- @Override
- public void onRemoved(ConversationModel conversationModel) {
- if (conversationModel != null) {
- boolean itsMyConversation = false;
- if (contactModel != null) {
- itsMyConversation = (conversationModel.getContact() != null
- && TestUtil.compare(conversationModel.getContact().getIdentity(), contactModel.getIdentity()));
- } else if (distributionListModel!= null) {
- itsMyConversation = conversationModel.getDistributionList() != null
- && TestUtil.compare(conversationModel.getDistributionList().getId(), distributionListModel.getId());
- } else if (groupModel != null) {
- itsMyConversation = conversationModel.getGroup() != null
- && TestUtil.compare(conversationModel.getGroup().getId(), groupModel.getId());
- }
- if (itsMyConversation) {
- RuntimeUtil.runOnUiThread(() -> {
- if (getActivity() != null) {
- getActivity().finish();
- }
- });
- }
- }
- }
- @Override
- public void onModifiedAll() {}
- };
- private final MessagePlayerListener messagePlayerListener = new MessagePlayerListener() {
- @Override
- public void onAudioStreamChanged(int newStreamType) { }
- @Override
- public void onAudioPlayEnded(AbstractMessageModel messageModel) {
- // Play next audio message, if any
- RuntimeUtil.runOnUiThread(() -> {
- if (composeMessageAdapter != null) {
- int index = composeMessageAdapter.getNextVoiceMessage(messageModel);
- if (index != AbsListView.INVALID_POSITION) {
- View view = composeMessageAdapter.getView(index, null, null);
- ComposeMessageHolder holder = (ComposeMessageHolder) view.getTag();
- if (holder.messagePlayer != null) {
- holder.messagePlayer.open();
- composeMessageAdapter.notifyDataSetChanged();
- }
- }
- }
- });
- }
- };
- private final QRCodeScanListener qrCodeScanListener = new QRCodeScanListener() {
- @Override
- public void onScanCompleted(String scanResult) {
- if (scanResult != null && scanResult.length() > 0) {
- RuntimeUtil.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if (messageText != null) {
- messageText.setText(scanResult);
- messageText.setSelection(messageText.length());
- }
- }
- });
- }
- }
- };
- private final BallotListener ballotListener = new BallotListener() {
- @Override
- public void onClosed(BallotModel ballotModel) { }
- @Override
- public void onModified(BallotModel ballotModel) { }
- @Override
- public void onCreated(BallotModel ballotModel) {
- try {
- BallotUtil.openDefaultActivity(
- getContext(),
- getFragmentManager(),
- ballotService.get(ballotModel.getId()),
- userService.getIdentity());
- } catch (Exception e) {
- logger.error("Exception", e);
- }
- }
- @Override
- public void onRemoved(BallotModel ballotModel) {
- }
- @Override
- public boolean handle(BallotModel ballotModel) {
- return ballotModel != null && userService.getIdentity().equals(ballotModel.getCreatorIdentity());
- }
- };
- @SuppressLint("StaticFieldLeak")
- @Override
- public void onRefresh() {
- logger.debug("onRefresh");
- if (actionMode != null || searchActionMode != null) {
- swipeRefreshLayout.setRefreshing(false);
- return;
- }
- new AsyncTask<Void, Void, Boolean>() {
- private List<AbstractMessageModel> messageModels;
- @Override
- protected Boolean doInBackground(Void... params) {
- messageModels = getNextRecords();
- if (messageModels != null) {
- return messageModels.size() >= nextMessageFilter.getPageSize();
- }
- return false;
- }
- @Override
- protected void onPostExecute(Boolean hasMoreRecords) {
- if (messageModels != null) {
- int numberOfInsertedRecords = insertToList(messageModels, false, true, true);
- if (numberOfInsertedRecords > 0) {
- convListView.setSelection(convListView.getSelectedItemPosition() + numberOfInsertedRecords + 1);
- }
- } else {
- composeMessageAdapter.notifyDataSetChanged();
- }
- // Notify PullToRefreshAttacher that the refresh has activity.finished
- swipeRefreshLayout.setRefreshing(false);
- swipeRefreshLayout.setEnabled(hasMoreRecords);
- }
- }.execute();
- }
- @Override
- public void onAttach(@NonNull Activity activity) {
- ((FragmentActivity) activity).getLifecycle().addObserver(this);
- logger.debug("onAttach");
- super.onAttach(activity);
- setHasOptionsMenu(true);
- this.activity = (ComposeMessageActivity) activity;
- this.audioManager = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE);
- if (bottomPanel != null) {
- bottomPanel.setVisibility(View.VISIBLE);
- }
- if (this.emojiPicker != null) {
- this.emojiPicker.init(activity);
- }
- // resolution and layout may have changed after being attached to a new activity
- ConfigUtils.getPreferredThumbnailWidth(activity, true);
- ConfigUtils.getPreferredAudioMessageWidth(activity, true);
- }
- @Override
- public void onCreate(Bundle savedInstanceState) {
- logger.debug("onCreate");
- if (getActivity() != null) {
- getActivity().supportPostponeEnterTransition();
- }
- super.onCreate(savedInstanceState);
- setRetainInstance(true);
- ListenerManager.contactTypingListeners.add(this.contactTypingListener);
- ListenerManager.messageListeners.add(this.messageListener, true);
- ListenerManager.groupListeners.add(this.groupListener);
- ListenerManager.contactListeners.add(this.contactListener);
- ListenerManager.conversationListeners.add(this.conversationListener);
- ListenerManager.messagePlayerListener.add(this.messagePlayerListener);
- ListenerManager.qrCodeScanListener.add(this.qrCodeScanListener);
- ListenerManager.ballotListeners.add(this.ballotListener);
- VoipListenerManager.callEventListener.add(this.voipCallEventListener);
- }
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- logger.debug("onCreateView");
- if (!requiredInstances()) {
- activity.finish();
- return this.fragmentView;
- }
- this.layoutInflater = inflater;
- if (this.fragmentView == null) {
- // set font size
- activity.getTheme().applyStyle(preferenceService.getFontStyle(), true);
- this.fragmentView = inflater.inflate(R.layout.fragment_compose_message, container, false);
- ScrollView sv = fragmentView.findViewById(R.id.wallpaper_scroll);
- sv.setEnabled(false);
- sv.setOnTouchListener(null);
- sv.setOnClickListener(null);
- this.convListView = fragmentView.findViewById(R.id.history);
- ViewCompat.setNestedScrollingEnabled(this.convListView, true);
- this.convListView.setDivider(null);
- this.convListView.setClipToPadding(false);
- if (ConfigUtils.isTabletLayout()) {
- this.convListView.setPadding(0, 0, 0, 0);
- }
- this.listViewTop = this.convListView.getPaddingTop();
- this.swipeRefreshLayout = fragmentView.findViewById(R.id.ptr_layout);
- this.swipeRefreshLayout.setOnRefreshListener(this);
- this.swipeRefreshLayout.setColorSchemeResources(R.color.accent_light);
- this.swipeRefreshLayout.setSize(SwipeRefreshLayout.LARGE);
- this.coordinatorLayout = fragmentView.findViewById(R.id.coordinator);
- this.messageText = fragmentView.findViewById(R.id.embedded_text_editor);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- // do not add on lollipop or lower due to this bug: https://issuetracker.google.com/issues/36937508
- this.messageText.setCustomSelectionActionModeCallback(textSelectionCallback);
- }
- this.sendButton = this.fragmentView.findViewById(R.id.send_button);
- this.attachButton = this.fragmentView.findViewById(R.id.attach_button);
- this.cameraButton = this.fragmentView.findViewById(R.id.camera_button);
- this.cameraButton.setOnClickListener(v -> {
- if (actionMode != null) {
- actionMode.finish();
- }
- closeQuoteMode();
- if (!validateSendingPermission()) {
- return;
- }
- if (ConfigUtils.requestCameraPermissions(activity, this, PERMISSION_REQUEST_ATTACH_CAMERA)) {
- attachCamera();
- }
- });
- updateCameraButton();
- this.emojiButton = this.fragmentView.findViewById(R.id.emoji_button);
- this.emojiButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- logger.info("Emoji button clicked");
- if (activity.isSoftKeyboardOpen()) {
- logger.info("Show emoji picker after keyboard close");
- activity.runOnSoftKeyboardClose(new Runnable() {
- @Override
- public void run() {
- if (emojiPicker != null) {
- emojiPicker.show(activity.loadStoredSoftKeyboardHeight());
- }
- }
- });
- messageText.post(new Runnable() {
- @Override
- public void run() {
- EditTextUtil.hideSoftKeyboard(messageText);
- }
- });
- } else {
- if (emojiPicker != null) {
- if (emojiPicker.isShown()) {
- logger.info("EmojPicker currently shown. Closing.");
- if (ConfigUtils.isLandscape(activity) &&
- !ConfigUtils.isTabletLayout() &&
- preferenceService.isFullscreenIme()) {
- emojiPicker.hide();
- } else {
- activity.openSoftKeyboard(emojiPicker, messageText);
- if (activity.getResources().getConfiguration().keyboard == Configuration.KEYBOARD_QWERTY) {
- emojiPicker.hide();
- }
- }
- } else {
- logger.info("Show emoji picker immediately");
- emojiPicker.show(activity.loadStoredSoftKeyboardHeight());
- }
- }
- }
- }
- });
- this.emojiMarkupUtil = EmojiMarkupUtil.getInstance();
- this.wallpaperView = this.fragmentView.findViewById(R.id.wallpaper_view);
- this.quickscrollUpView = this.fragmentView.findViewById(R.id.quickscroll_top);
- this.quickscrollDownView = this.fragmentView.findViewById(R.id.quickscroll_bottom);
- this.dateView = this.fragmentView.findViewById(R.id.date_separator_container);
- this.dateTextView = this.fragmentView.findViewById(R.id.text_view);
- quoteInfo.quotePanel = this.fragmentView.findViewById(R.id.quote_panel);
- quoteInfo.quoteTextView = this.fragmentView.findViewById(R.id.quote_text_view);
- quoteInfo.quoteIdentityView = this.fragmentView.findViewById(R.id.quote_id_view);
- quoteInfo.quoteBar = this.fragmentView.findViewById(R.id.quote_bar);
- quoteInfo.quoteThumbnail = this.fragmentView.findViewById(R.id.quote_thumbnail);
- quoteInfo.quoteTypeImage = this.fragmentView.findViewById(R.id.quote_type_image);
- ImageView quoteCloseButton = this.fragmentView.findViewById(R.id.quote_panel_close_button);
- quoteCloseButton.setOnClickListener(v -> closeQuoteMode());
- this.bottomPanel = this.fragmentView.findViewById(R.id.bottom_panel);
- this.openBallotNoticeView = this.fragmentView.findViewById(R.id.open_ballots_layout);
- this.openGroupRequestNoticeView = this.fragmentView.findViewById(R.id.open_group_requests_layout);
- this.getValuesFromBundle(savedInstanceState);
- this.handleIntent(activity.getIntent());
- this.setupListeners();
- }
- if (preferenceService.getEmojiStyle() == PreferenceService.EmojiStyle_ANDROID) {
- // remove emoji button
- this.emojiButton.setVisibility(View.GONE);
- this.messageText.setPadding(getResources().getDimensionPixelSize(R.dimen.no_emoji_button_padding_left), this.messageText.getPaddingTop(), this.messageText.getPaddingRight(), this.messageText.getPaddingBottom());
- } else {
- try {
- this.emojiPicker = (EmojiPicker) ((ViewStub) this.activity.findViewById(R.id.emoji_stub)).inflate();
- this.emojiPicker.init(activity);
- this.emojiButton.attach(this.emojiPicker, preferenceService.isFullscreenIme());
- this.emojiPicker.setEmojiKeyListener(new EmojiPicker.EmojiKeyListener() {
- @Override
- public void onBackspaceClick() {
- messageText.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
- }
- @Override
- public void onEmojiClick(String emojiCodeString) {
- RuntimeUtil.runOnUiThread(() -> messageText.addEmoji(emojiCodeString));
- }
- });
- this.emojiPicker.addEmojiPickerListener(this);
- } catch (Exception e) {
- logger.error("Exception", e);
- activity.finish();
- }
- }
- return this.fragmentView;
- }
- private final android.view.ActionMode.Callback textSelectionCallback = new android.view.ActionMode.Callback() {
- private final Pattern pattern = Pattern.compile("\\B");
- @Override
- public boolean onCreateActionMode(android.view.ActionMode mode, Menu menu) {
- return true;
- }
- @Override
- public boolean onPrepareActionMode(android.view.ActionMode mode, Menu menu) {
- menu.removeGroup(CONTEXT_MENU_GROUP);
- if (messageText != null && messageText.getText() != null) {
- String text = messageText.getText().toString();
- if (text.length() > 1) {
- int start = messageText.getSelectionStart();
- int end = messageText.getSelectionEnd();
- try {
- if ((start <= 0 || pattern.matcher(text.substring(start - 1, start)).find()) &&
- (end >= text.length() || pattern.matcher(text.substring(end, end + 1)).find()) &&
- !text.substring(start, end).contains("\n")) {
- menu.add(CONTEXT_MENU_GROUP, CONTEXT_MENU_BOLD, 200, R.string.bold);
- menu.add(CONTEXT_MENU_GROUP, CONTEXT_MENU_ITALIC, 201, R.string.italic);
- menu.add(CONTEXT_MENU_GROUP, CONTEXT_MENU_STRIKETHRU, 203, R.string.strikethrough);
- }
- } catch (StringIndexOutOfBoundsException e) {
- // do not add menus
- }
- }
- }
- return true;
- }
- @Override
- public boolean onActionItemClicked(android.view.ActionMode mode, MenuItem item) {
- switch (item.getItemId()) {
- case CONTEXT_MENU_BOLD:
- addMarkup("*");
- break;
- case CONTEXT_MENU_ITALIC:
- addMarkup("_");
- break;
- case CONTEXT_MENU_STRIKETHRU:
- addMarkup("~");
- break;
- default:
- return false;
- }
- return true;
- }
- @Override
- public void onDestroyActionMode(android.view.ActionMode mode) {
- }
- };
- private void addMarkup(String string) {
- Editable editable = messageText.getText();
- if (editable.length() > 0) {
- int start = messageText.getSelectionStart();
- int end = messageText.getSelectionEnd();
- editable.insert(end, string);
- editable.insert(start, string);
- }
- messageText.invalidate();
- }
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- logger.debug("onActivityCreated");
- super.onActivityCreated(savedInstanceState);
- /*
- * This callback tells the fragment when it is fully associated with the new activity instance. This is called after onCreateView(LayoutInflater, ViewGroup, Bundle) and before onViewStateRestored(Bundle).
- */
- activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
- if (preferenceService == null) {
- return;
- }
- if (preferenceService.getEmojiStyle() != PreferenceService.EmojiStyle_ANDROID) {
- try {
- View rootView = activity.getWindow().getDecorView().getRootView();
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
- try {
- ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
- if (decorView.getChildCount() == 1 && decorView.getChildAt(0) instanceof LinearLayout) {
- rootView = decorView.getChildAt(0);
- }
- } catch (Exception e) {
- logger.error("Exception", e);
- }
- }
- ViewCompat.setOnApplyWindowInsetsListener(rootView, new OnApplyWindowInsetsListener() {
- @Override
- public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
- logger.debug("system window top " + insets.getSystemWindowInsetTop() + " bottom " + insets.getSystemWindowInsetBottom());
- logger.debug("stable insets top " + insets.getStableInsetTop() + " bottom " + insets.getStableInsetBottom());
- if (insets.getSystemWindowInsetBottom() <= insets.getStableInsetBottom()) {
- activity.onSoftKeyboardClosed();
- } else {
- activity.onSoftKeyboardOpened(insets.getSystemWindowInsetBottom() - insets.getStableInsetBottom());
- }
- return insets;
- }
- });
- } catch (NullPointerException e) {
- logger.error("Exception", e);
- }
- activity.addOnSoftKeyboardChangedListener(this);
- }
- // restore action mode after rotate if the activity was detached
- if (convListView != null && convListView.getCheckedItemCount() > 0 && actionMode != null) {
- actionMode = ((AppCompatActivity) getActivity()).startSupportActionMode(new ComposeMessageAction(this.longClickItem));
- }
- }
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- activity.supportStartPostponedEnterTransition();
- }
- @Override
- public void onResume(@NonNull LifecycleOwner owner) {
- logger.debug("onResume");
- //set visible receiver
- if (this.messageReceiver != null) {
- this.notificationService.setVisibleReceiver(this.messageReceiver);
- isPaused = false;
- logger.debug("markAllRead");
- // mark all unread messages
- if (this.unreadMessages.size() > 0) {
- ReadMessagesRoutine r = new ReadMessagesRoutine(this.unreadMessages,
- this.messageService,
- this.notificationService);
- r.addOnFinished(new ReadMessagesRoutine.OnFinished() {
- @Override
- public void finished(boolean success) {
- if (success) {
- unreadMessages.clear();
- }
- }
- });
- new Thread(r).start();
- }
- // update menus
- updateMuteMenu();
- // start media players again
- this.messagePlayerService.resumeAll(getActivity(), this.messageReceiver, SOURCE_LIFECYCLE);
- // make sure to remark the active chat
- if (ConfigUtils.isTabletLayout()) {
- ListenerManager.chatListener.handle(listener -> listener.onChatOpened(this.conversationUid));
- }
- // restore scroll position after orientation change
- if (getActivity() != null) {
- Intent intent = getActivity().getIntent();
- if (intent != null && !intent.hasExtra(EXTRA_API_MESSAGE_ID) && !intent.hasExtra(EXTRA_SEARCH_QUERY)) {
- convListView.post(new Runnable() {
- @Override
- public void run() {
- if (listInstancePosition != AbsListView.INVALID_POSITION &&
- messageReceiver != null &&
- messageReceiver.getUniqueIdString().equals(listInstanceReceiverId)) {
- logger.debug("restoring position " + listInstancePosition);
- convListView.setSelectionFromTop(listInstancePosition, listInstanceTop);
- } else {
- jumpToFirstUnreadMessage();
- }
- // make sure it's not restored twice
- listInstancePosition = AbsListView.INVALID_POSITION;
- listInstanceReceiverId = null;
- }
- });
- }
- }
- // update group requests as they could have been changed when coming back from the overview activity
- if (ConfigUtils.supportsGroupLinks()) {
- this.openGroupRequestNoticeView.updateGroupRequests();
- }
- }
- }
- @Override
- public void onPause(@NonNull LifecycleOwner owner) {
- logger.debug("onPause");
- isPaused = true;
- onEmojiPickerClose();
- if (this.notificationService != null) {
- this.notificationService.setVisibleReceiver(null);
- }
- //stop all playing audio messages (incoming call?)
- if (this.messagePlayerService != null) {
- this.messagePlayerService.pauseAll(SOURCE_LIFECYCLE);
- }
- // save unfinished text
- saveMessageDraft();
- if(this.typingIndicatorTextWatcher != null) {
- this.typingIndicatorTextWatcher.stopTyping();
- }
- preserveListInstanceValues();
- }
- @Override
- public void onStop() {
- logger.debug("onStop");
- if(this.typingIndicatorTextWatcher != null) {
- this.typingIndicatorTextWatcher.stopTyping();
- }
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {
- // close keyboard to prevent layout corruption after unlocking phone
- if (this.messageText != null) {
- EditTextUtil.hideSoftKeyboard(this.messageText);
- }
- }
- super.onStop();
- }
- @Override
- public void onDetach() {
- logger.debug("onDetach");
- if (this.emojiPicker != null && this.emojiPicker.isShown()) {
- this.emojiPicker.hide();
- }
- dismissMentionPopup();
- this.activity = null;
- super.onDetach();
- }
- @Override
- public void onDestroy() {
- logger.debug("onDestroy");
- try {
- ListenerManager.contactTypingListeners.remove(this.contactTypingListener);
- ListenerManager.groupListeners.remove(this.groupListener);
- ListenerManager.messageListeners.remove(this.messageListener);
- ListenerManager.contactListeners.remove(this.contactListener);
- ListenerManager.conversationListeners.remove(this.conversationListener);
- ListenerManager.messagePlayerListener.remove(this.messagePlayerListener);
- ListenerManager.qrCodeScanListener.remove(this.qrCodeScanListener);
- ListenerManager.ballotListeners.remove(this.ballotListener);
- VoipListenerManager.callEventListener.remove(this.voipCallEventListener);
- dismissTooltipPopup(workTooltipPopup, true);
- workTooltipPopup = null;
- dismissMentionPopup();
- if (this.emojiButton != null) {
- this.emojiButton.detach(this.emojiPicker);
- }
- if (this.emojiPicker != null) {
- this.emojiPicker.removeEmojiPickerListener(this);
- }
- if (!requiredInstances()) {
- super.onDestroy();
- return;
- }
- //release all players!
- if (this.messagePlayerService != null) {
- this.messagePlayerService.release();
- }
- if (this.messageService != null) {
- this.messageService.saveMessageQueueAsync();
- }
- if (this.thumbnailCache != null) {
- this.thumbnailCache.flush();
- }
- if (this.messageText != null) {
- //remove typing change listener
- if(this.typingIndicatorTextWatcher != null) {
- this.messageText.removeTextChangedListener(this.typingIndicatorTextWatcher);
- }
- // http://stackoverflow.com/questions/18348049/android-edittext-memory-leak
- this.messageText.setText(null);
- }
- //remove wallpaper
- this.wallpaperView.setImageBitmap(null);
- // delete pending deleteable messages
- deleteDeleteableMessages();
- if (this.deleteSnackbar != null && this.deleteSnackbar.isShownOrQueued()) {
- this.deleteSnackbar.dismiss();
- }
- removeIsTypingFooter();
- this.isTypingView = null;
- //clear all records to remove all references
- if(this.composeMessageAdapter != null) {
- this.composeMessageAdapter.clear();
- this.composeMessageAdapter = null;
- }
- } catch (Exception x) {
- logger.error("Exception", x);
- }
- super.onDestroy();
- }
- private void removeScrollButtons() {
- logger.debug("removeScrollButtons");
- if (dateView != null && dateView.getVisibility() == View.VISIBLE) {
- AnimationUtil.slideOutAnimation(dateView, false, 1f, null);
- }
- if (actionMode != null) {
- actionMode.finish();
- }
- }
- private void setupListeners() {
- // Setting this scroll listener is required to ensure that during ListView scrolling,
- // we don't look for swipes or pulldowns
- this.convListView.setOnScrollListener(new AbsListView.OnScrollListener() {
- @Override
- public void onScrollStateChanged(AbsListView absListView, int scrollState) {
- if (listViewSwipeListener != null) {
- listViewSwipeListener.setEnabled(scrollState != AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
- }
- if (!absListView.canScrollList(View.SCROLL_AXIS_VERTICAL)) {
- AnimationUtil.setFadingVisibility(quickscrollDownView, View.GONE);
- }
- if (!absListView.canScrollList(-View.SCROLL_AXIS_VERTICAL)) {
- AnimationUtil.setFadingVisibility(quickscrollUpView, View.GONE);
- }
- }
- @Override
- public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
- if(view != null && view.getChildCount() > 0) {
- View itemView = view.getChildAt(0);
- boolean onTop = firstVisibleItem == 0 && itemView.getTop() == listViewTop;
- swipeRefreshLayout.setEnabled(onTop);
- if (firstVisibleItem != lastFirstVisibleItem) {
- if (lastFirstVisibleItem < firstVisibleItem) {
- // scrolling down
- AnimationUtil.setFadingVisibility(quickscrollUpView, View.GONE);
- if (view.canScrollList(View.SCROLL_AXIS_VERTICAL)) {
- AnimationUtil.setFadingVisibility(quickscrollDownView, View.VISIBLE);
- }
- } else {
- // scrolling up
- AnimationUtil.setFadingVisibility(quickscrollDownView, View.GONE);
- if (view.canScrollList(-View.SCROLL_AXIS_VERTICAL)) {
- AnimationUtil.setFadingVisibility(quickscrollUpView, View.VISIBLE);
- }
- }
- lastFirstVisibleItem = firstVisibleItem;
- if (dateView.getVisibility() != View.VISIBLE && composeMessageAdapter != null && composeMessageAdapter.getCount() > 0) {
- AnimationUtil.slideInAnimation(dateView, false, 200);
- }
- dateViewHandler.removeCallbacks(dateViewTask);
- dateViewHandler.postDelayed(dateViewTask, SCROLLBUTTON_VIEW_TIMEOUT);
- if (composeMessageAdapter != null) {
- AbstractMessageModel abstractMessageModel = composeMessageAdapter.getItem(firstVisibleItem);
- if (abstractMessageModel != null) {
- final Date createdAt = abstractMessageModel.getCreatedAt();
- if (createdAt != null) {
- final String text = LocaleUtil.formatDateRelative(getActivity(), createdAt.getTime());
- dateTextView.setText(text);
- dateView.post(() -> {
- dateTextView.setText(text);
- });
- }
- }
- }
- }
- }
- else {
- swipeRefreshLayout.setEnabled(false);
- }
- }
- });
- listViewSwipeListener = new ListViewSwipeListener(
- this.convListView,
- new ListViewSwipeListener.DismissCallbacks() {
- @Override
- public boolean canSwipe(int position) {
- if (actionMode != null) {
- return false;
- }
- if (messageReceiver == null || !messageReceiver.validateSendingPermission(null)) {
- return false;
- }
- int viewType = composeMessageAdapter.getItemViewType(position);
- if (viewType == ComposeMessageAdapter.TYPE_STATUS ||
- viewType == ComposeMessageAdapter.TYPE_FIRST_UNREAD ||
- viewType == ComposeMessageAdapter.TYPE_DATE_SEPARATOR) {
- return false;
- }
- AbstractMessageModel messageModel = composeMessageAdapter.getItem(position);
- if (messageModel == null) {
- return false;
- }
- return QuoteUtil.isQuoteable(messageModel);
- }
- @Override
- public void onSwiped(int position) {
- if (composeMessageAdapter == null) {
- return;
- }
- AbstractMessageModel abstractMessageModel = composeMessageAdapter.getItem(position);
- if (preferenceService.isInAppVibrate()) {
- if (isAdded() && !isDetached() && activity != null) {
- Vibrator vibrator = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE);
- if (vibrator != null && vibrator.hasVibrator()) {
- vibrator.vibrate(100);
- }
- }
- }
- if (abstractMessageModel != null) {
- if (isQuotePanelShown() && abstractMessageModel.equals(quoteInfo.messageModel)) {
- closeQuoteMode();
- } else {
- startQuoteMode(abstractMessageModel, new Runnable() {
- @Override
- public void run() {
- RuntimeUtil.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- EditTextUtil.showSoftKeyboard(messageText);
- }
- });
- }
- });
- }
- }
- }
- }
- );
- this.quickscrollDownView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- removeScrollButtons();
- scrollList(Integer.MAX_VALUE);
- }
- });
- this.quickscrollUpView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- removeScrollButtons();
- scrollList(0);
- }
- });
- if (sendButton != null) {
- sendButton.setOnClickListener(new DebouncedOnClickListener(500) {
- @Override
- public void onDebouncedClick(View v) {
- sendMessage();
- }
- });
- }
- if (attachButton != null) {
- attachButton.setOnClickListener(new DebouncedOnClickListener(1000) {
- @Override
- public void onDebouncedClick(View v) {
- if (validateSendingPermission()) {
- if (actionMode != null) {
- actionMode.finish();
- }
- closeQuoteMode();
- Intent intent = new Intent(activity, MediaAttachActivity.class);
- IntentDataUtil.addMessageReceiverToIntent(intent, messageReceiver);
- if (ComposeMessageFragment.this.lastMediaFilter != null) {
- intent = IntentDataUtil.addLastMediaFilterToIntent(intent, ComposeMessageFragment.this.lastMediaFilter);
- }
- activity.startActivityForResult(intent, ThreemaActivity.ACTIVITY_ID_ATTACH_MEDIA);
- activity.overridePendingTransition(R.anim.fast_fade_in, R.anim.fast_fade_out);
- }
- }
- });
- }
- this.messageText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
- @Override
- public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
- if ((actionId == EditorInfo.IME_ACTION_SEND) ||
- (event != null && event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_ENTER && preferenceService.isEnterToSend())) {
- sendMessage();
- return true;
- }
- return false;
- }
- });
- if (ConfigUtils.isDefaultEmojiStyle()) {
- this.messageText.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (emojiPicker != null) {
- if (emojiPicker.isShown()) {
- if (ConfigUtils.isLandscape(activity) &&
- !ConfigUtils.isTabletLayout() &&
- preferenceService.isFullscreenIme()) {
- emojiPicker.hide();
- } else {
- activity.openSoftKeyboard(emojiPicker, messageText);
- }
- }
- }
- }
- });
- }
- this.messageText.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) {
- ThreemaApplication.activityUserInteract(activity);
- updateSendButton(s);
- if (isGroupChat && s.length() > 0 && getActivity() != null && getActivity().getCurrentFocus() == messageText) {
- checkPossibleMention(s, start, before, count);
- }
- }
- @Override
- public void afterTextChanged(Editable s) {
- updateCameraButton();
- }
- });
- }
- private void updateCameraButton() {
- if (cameraButton == null || messageText == null) {
- return;
- }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
- ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
- // shouldShowRequestPermissionRationale returns false if
- // a) the user selected "never ask again"; or
- // b) a permission dialog has never been shown
- // we hide the camera button only in case a)
- if (!shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) && preferenceService.getCameraPermissionRequestShown()) {
- cameraButton.setVisibility(View.GONE);
- fixMessageTextPadding(View.GONE);
- return;
- }
- }
- int visibility = messageText.getText() == null ||
- messageText.getText().length() == 0 ?
- View.VISIBLE : View.GONE;
- if (cameraButton.getVisibility() != visibility) {
- Transition transition = new Slide(Gravity.RIGHT);
- transition.setDuration(150);
- transition.setInterpolator(new LinearInterpolator());
- transition.addTarget(R.id.camera_button);
- TransitionManager.beginDelayedTransition((ViewGroup) cameraButton.getParent(), transition);
- cameraButton.setVisibility(visibility);
- fixMessageTextPadding(visibility);
- }
- }
- private void fixMessageTextPadding(int visibility) {
- int marginRight = getResources().getDimensionPixelSize(visibility == View.VISIBLE ? R.dimen.emoji_and_photo_button_width : R.dimen.emoji_button_width);
- messageText.setPadding(messageText.getPaddingLeft(), messageText.getPaddingTop(), marginRight, messageText.getPaddingBottom());
- }
- private void checkPossibleMention(CharSequence s, int start, int before, int count) {
- if (count == 1 && s.charAt(start) == '@' && start < s.length() // if current char is @ and only if adding a new char
- && (start == 0 || s.charAt(start - 1) == ' ' || s.charAt(start - 1) == '\n') // and only show if at start or if there is empty space before to not interrupt typing mail addresses
- && (s.length() <= start + 1 || s.charAt(start + 1) == ' ' || s.charAt(start + 1) == '\n') // and only show if @ is at the very end or also has empty space in the back.
- )
- {
- dismissTooltipPopup(workTooltipPopup, true);
- workTooltipPopup = null;
- dismissMentionPopup();
- mentionPopup = new MentionSelectorPopup(getActivity(), this, groupService, this.contactService, this.userService, this.preferenceService, groupModel);
- mentionPopup.show(getActivity(), this.messageText, emojiButton.getWidth());
- }
- }
- private void updateSendButton(CharSequence s) {
- if (isQuotePanelShown()) {
- if (TestUtil.empty(s)) {
- sendButton.setEnabled(false);
- } else {
- sendButton.setSend();
- sendButton.setEnabled(true);
- }
- } else {
- if (TestUtil.empty(s)) {
- sendButton.setRecord();
- sendButton.setEnabled(true);
- } else {
- sendButton.setSend();
- sendButton.setEnabled(true);
- }
- }
- if (emojiButton != null) emojiButton.setVisibility(preferenceService.getEmojiStyle() != PreferenceService.EmojiStyle_ANDROID ? View.VISIBLE : View.GONE);
- if (messageText != null) messageText.setVisibility(View.VISIBLE);
- }
- private void setBackgroundWallpaper() {
- if (isAdded() && this.wallpaperView != null) {
- wallpaperService.setupWallpaperBitmap(this.messageReceiver, this.wallpaperView, ConfigUtils.isLandscape(activity));
- }
- }
- private void resetDefaultValues() {
- this.distributionListId = 0;
- this.groupId = 0;
- this.identity = null;
- this.groupModel = null;
- this.distributionListModel = null;
- this.contactModel = null;
- this.messageReceiver = null;
- this.listInstancePosition = AbsListView.INVALID_POSITION;
- this.listInstanceReceiverId = null;
- if (ConfigUtils.isTabletLayout()) {
- // apply pending deletes upon reentering a chat through onNewIntent() in multi-frame environment
- deleteDeleteableMessages();
- if (this.deleteSnackbar != null && this.deleteSnackbar.isShownOrQueued()) {
- this.deleteSnackbar.dismiss();
- }
- closeQuoteMode();
- }
- }
- private void getValuesFromBundle(Bundle bundle) {
- if (bundle != null) {
- this.groupId = bundle.getInt(ThreemaApplication.INTENT_DATA_GROUP, 0);
- this.distributionListId = bundle.getInt(ThreemaApplication.INTENT_DATA_DISTRIBUTION_LIST, 0);
- this.identity = bundle.getString(ThreemaApplication.INTENT_DATA_CONTACT);
- this.intentTimestamp = bundle.getLong(ThreemaApplication.INTENT_DATA_TIMESTAMP, 0L);
- this.cameraUri = bundle.getParcelable(CAMERA_URI);
- }
- }
- public void onNewIntent(Intent intent) {
- logger.debug("onNewIntent");
- if (!requiredInstances()) {
- return;
- }
- resetDefaultValues();
- handleIntent(intent);
- // initialize various toolbar items
- if (actionMode != null) {
- actionMode.finish();
- }
- if (searchActionMode != null) {
- searchActionMode.finish();
- }
- this.closeQuoteMode();
- this.updateToolbarTitle();
- this.updateMenus();
- }
- private void setupToolbar() {
- View actionBarTitleView = layoutInflater.inflate(R.layout.actionbar_compose_title, null);
- if (actionBarTitleView != null) {
- this.actionBarTitleTextView = actionBarTitleView.findViewById(R.id.title);
- this.actionBarSubtitleImageView = actionBarTitleView.findViewById(R.id.subtitle_image);
- this.actionBarSubtitleTextView = actionBarTitleView.findViewById(R.id.subtitle_text);
- this.actionBarAvatarView = actionBarTitleView.findViewById(R.id.avatar_view);
- final RelativeLayout actionBarTitleContainer = actionBarTitleView.findViewById(R.id.title_container);
- actionBarTitleContainer.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent intent = null;
- if (isGroupChat) {
- if (groupService.isGroupMember(groupModel)) {
- intent = groupService.getGroupEditIntent(groupModel, activity);
- }
- } else if (isDistributionListChat) {
- intent = new Intent(activity, DistributionListAddActivity.class);
- } else {
- intent = new Intent(activity, ContactDetailActivity.class);
- intent.putExtra(ThreemaApplication.INTENT_DATA_CONTACT_READONLY, true);
- }
- if (intent != null) {
- intent = addExtrasToIntent(intent, messageReceiver);
- activity.startActivityForResult(intent, 0);
- }
- }
- });
- if (contactModel != null) {
- if (contactModel.getIdentityType() == IdentityType.WORK) {
- if (!ConfigUtils.isWorkBuild()) {
- if (!preferenceService.getIsWorkHintTooltipShown()) {
- actionBarTitleTextView.postDelayed(() -> {
- if (getActivity() != null && isAdded()) {
- dismissTooltipPopup(workTooltipPopup, true);
- int[] location = new int[2];
- actionBarAvatarView.getLocationOnScreen(location);
- location[0] += actionBarAvatarView.getWidth() / 2;
- location[1] += actionBarAvatarView.getHeight();
- workTooltipPopup = new TooltipPopup(getActivity(), R.string.preferences__tooltip_work_hint_shown, R.layout.popup_tooltip_top_left_work, this, new Intent(getActivity(), WorkExplainActivity.class));
- workTooltipPopup.show(getActivity(), actionBarAvatarView, getString(R.string.tooltip_work_hint), TooltipPopup.ALIGN_BELOW_ANCHOR_ARROW_LEFT, location, 4000);
- }
- }, 1000);
- }
- }
- }
- }
- }
- if (activity == null) {
- activity = (ComposeMessageActivity) getActivity();
- }
- if (activity != null) {
- this.actionBar = activity.getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_HOME_AS_UP);
- actionBar.setCustomView(actionBarTitleView);
- }
- }
- }
- @UiThread
- private void handleIntent(Intent intent) {
- logger.debug("handleIntent");
- this.isGroupChat = false;
- this.isDistributionListChat = false;
- this.currentPageReferenceId = null;
- //remove old indicator every time!
- //fix ANDR-432
- if (this.typingIndicatorTextWatcher != null) {
- if (this.messageText != null) {
- this.messageText.removeTextChangedListener(this.typingIndicatorTextWatcher);
- }
- }
- if (intent.hasExtra(ThreemaApplication.INTENT_DATA_GROUP) || this.groupId != 0) {
- this.isGroupChat = true;
- if (this.groupId == 0) {
- this.groupId = intent.getIntExtra(ThreemaApplication.INTENT_DATA_GROUP, 0);
- }
- this.groupModel = this.groupService.getById(this.groupId);
- if (this.groupModel == null || this.groupModel.isDeleted()) {
- logger.error(activity.getString(R.string.group_not_found), activity, new Runnable() {
- @Override
- public void run() {
- activity.finish();
- }
- });
- return;
- }
- intent.removeExtra(ThreemaApplication.INTENT_DATA_GROUP);
- this.messageReceiver = this.groupService.createReceiver(this.groupModel);
- this.conversationUid = ConversationUtil.getGroupConversationUid(this.groupId);
- if (ConfigUtils.supportsGroupLinks() && groupService.isGroupOwner(this.groupModel)) {
- this.openGroupRequestNoticeView.setGroupIdReference(this.groupId);
- this.openGroupRequestNoticeView.updateGroupRequests();
- }
- } else if (intent.hasExtra(ThreemaApplication.INTENT_DATA_DISTRIBUTION_LIST) || this.distributionListId != 0) {
- this.isDistributionListChat = true;
- try {
- if (this.distributionListId == 0) {
- this.distributionListId = intent.getIntExtra(ThreemaApplication.INTENT_DATA_DISTRIBUTION_LIST, 0);
- }
- this.distributionListModel = distributionListService.getById(this.distributionListId);
- if (this.distributionListModel == null) {
- logger.error("Invalid distribution list", activity, new Runnable() {
- @Override
- public void run() {
- activity.finish();
- }
- });
- return;
- }
- intent.removeExtra(ThreemaApplication.INTENT_DATA_DISTRIBUTION_LIST);
- this.messageReceiver = distributionListService.createReceiver(this.distributionListModel);
- } catch (Exception e) {
- logger.error("Exception", e);
- return;
- }
- this.conversationUid = ConversationUtil.getDistributionListConversationUid(this.distributionListId);
- } else {
- if (TestUtil.empty(this.identity)) {
- this.identity = intent.getStringExtra(ThreemaApplication.INTENT_DATA_CONTACT);
- }
- if (this.identity == null) {
- if (intent.getData() != null) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) {
- this.identity = ContactUtil.getIdentityFromViewIntent(activity, intent);
- } else {
- Toast.makeText(activity, R.string.permission_contacts_required, Toast.LENGTH_LONG).show();
- }
- }
- }
- intent.removeExtra(ThreemaApplication.INTENT_DATA_CONTACT);
- if (this.identity == null || this.identity.length() == 0 || this.identity.equals(this.userService.getIdentity())) {
- logger.error("no identity found");
- activity.finish();
- return;
- }
- this.contactModel = this.contactService.getByIdentity(this.identity);
- if (this.contactModel == null) {
- Toast.makeText(getContext(), getString(R.string.contact_not_found) + ": " + this.identity, Toast.LENGTH_LONG).show();
- Intent homeIntent = new Intent(activity, HomeActivity.class);
- homeIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(homeIntent);
- activity.overridePendingTransition(0, 0);
- activity.finish();
- return;
- }
- this.messageReceiver = this.contactService.createReceiver(this.contactModel);
- this.typingIndicatorTextWatcher = new TypingIndicatorTextWatcher(this.userService, contactModel);
- this.conversationUid = ConversationUtil.getIdentityConversationUid(this.identity);
- }
- if (this.messageReceiver == null) {
- logger.error("invalid receiver", activity, new Runnable() {
- @Override
- public void run() {
- activity.finish();
- }
- });
- return;
- }
- // hide chat from view and prevent screenshots - may not work on some devices
- if (this.hiddenChatsListService.has(this.messageReceiver.getUniqueIdString())) {
- try {
- activity.getWindow().addFlags(FLAG_SECURE);
- } catch (Exception ignored) { }
- }
- // set wallpaper based on message receiver
- this.setBackgroundWallpaper();
- // report shortcut as used
- if (preferenceService.isDirectShare()) {
- try {
- ShortcutManagerCompat.reportShortcutUsed(activity, this.messageReceiver.getUniqueIdString());
- } catch (IllegalStateException e) {
- logger.debug("Failed to report shortcut use", e);
- }
- }
- this.initConversationList(intent.hasExtra(EXTRA_API_MESSAGE_ID) && intent.hasExtra(EXTRA_SEARCH_QUERY) ? (Runnable) () -> {
- String apiMessageId = intent.getStringExtra(EXTRA_API_MESSAGE_ID);
- String searchQuery = intent.getStringExtra(EXTRA_SEARCH_QUERY);
- AbstractMessageModel targetMessageModel = messageService.getMessageModelByApiMessageId(apiMessageId, messageReceiver.getType());
- if (targetMessageModel != null && !TestUtil.empty(apiMessageId) && !TestUtil.empty(searchQuery)) {
- String identity;
- if (targetMessageModel instanceof GroupMessageModel) {
- identity = targetMessageModel.isOutbox() ? contactService.getMe().getIdentity() : targetMessageModel.getIdentity();
- } else {
- identity = targetMessageModel.getIdentity();
- }
- QuoteUtil.QuoteContent quoteContent = QuoteUtil.QuoteContent.createV2(
- identity,
- searchQuery,
- searchQuery,
- apiMessageId,
- targetMessageModel,
- messageReceiver.getType(),
- null,
- null
- );
- if (composeMessageAdapter != null) {
- ComposeMessageAdapter.ConversationListFilter filter = (ComposeMessageAdapter.ConversationListFilter) composeMessageAdapter.getQuoteFilter(quoteContent);
- searchV2Quote(apiMessageId, filter);
- intent.removeExtra(EXTRA_API_MESSAGE_ID);
- }
- } else {
- Toast.makeText(ThreemaApplication.getAppContext(), R.string.message_not_found, Toast.LENGTH_SHORT).show();
- }
- } : null);
- // work around the problem that the same original intent may be sent
- // each time a singleTop activity (like this one) is coming back to front
- // causing - in this case - duplicate delivery of forwarded messages.
- // so we make sure the intent is only handled if it's newer than
- // any previously handled intent
- long newTimestamp = 0L;
- try {
- newTimestamp = intent.getLongExtra(ThreemaApplication.INTENT_DATA_TIMESTAMP, 0L);
- if (newTimestamp != 0L && newTimestamp <= this.intentTimestamp) {
- return;
- }
- } finally {
- this.intentTimestamp = newTimestamp;
- }
- this.messageText.setText("");
- this.messageText.setMessageReceiver(this.messageReceiver);
- this.openBallotNoticeView.setMessageReceiver(this.messageReceiver);
- this.openBallotNoticeView.setVisibilityListener(this);
- // restore draft before setting predefined text
- restoreMessageDraft();
- String defaultText = intent.getStringExtra(ThreemaApplication.INTENT_DATA_TEXT);
- if (!TestUtil.empty(defaultText)) {
- this.messageText.setText(null);
- this.messageText.append(defaultText);
- }
- updateSendButton(this.messageText.getText());
- updateCameraButton();
- boolean editFocus = intent.getBooleanExtra(ThreemaApplication.INTENT_DATA_EDITFOCUS, false);
- if (editFocus || this.unreadCount <= 0) {
- messageText.setSelected(true);
- messageText.requestFocus();
- }
- this.notificationService.setVisibleReceiver(this.messageReceiver);
- if (!this.isGroupChat && !this.isDistributionListChat) {
- this.messageText.addTextChangedListener(this.typingIndicatorTextWatcher);
- }
- ListenerManager.chatListener.handle(listener -> listener.onChatOpened(conversationUid));
- }
- private boolean validateSendingPermission() {
- return this.messageReceiver != null
- && this.messageReceiver.validateSendingPermission(new MessageReceiver.OnSendingPermissionDenied() {
- @Override
- public void denied(final int errorResId) {
- RuntimeUtil.runOnUiThread(() -> SingleToast.getInstance().showLongText(getString(errorResId)));
- }
- });
- }
- private void deleteSelectedMessages() {
- int deleteableMessagesCount = 0;
- if (selectedMessages != null && selectedMessages.size() > 0) {
- // sort highest first for removal
- Collections.sort(selectedMessages, new Comparator<AbstractMessageModel>() {
- @Override
- public int compare(AbstractMessageModel lhs, AbstractMessageModel rhs) {
- return rhs.getId() - lhs.getId();
- }
- });
- synchronized (deleteableMessages) {
- for (AbstractMessageModel messageModel : selectedMessages) {
- if (messageModel != null) {
- // remove from adapter but not from database
- int position = composeMessageAdapter.getPosition(messageModel);
- AbstractMessageModel previousMessage = null;
- if (position > 0) {
- previousMessage = composeMessageAdapter.getItem(position - 1);
- }
- if (previousMessage != null && previousMessage instanceof DateSeparatorMessageModel) {
- AbstractMessageModel nextMessage = null;
- if (position < (composeMessageAdapter.getCount() - 1)) {
- nextMessage = composeMessageAdapter.getItem(position + 1);
- }
- if (nextMessage == null ||
- !dayFormatter.format(messageModel.getCreatedAt()).equals(dayFormatter.format(nextMessage.getCreatedAt()))) {
- deleteableMessages.add(new Pair<>(previousMessage, position - 1));
- composeMessageAdapter.remove(previousMessage);
- }
- }
- deleteableMessages.add(new Pair<>(messageModel, position));
- deleteableMessagesCount++;
- composeMessageAdapter.remove(messageModel);
- }
- }
- composeMessageAdapter.notifyDataSetChanged();
- // sort lowest first for insertion
- Collections.sort(deleteableMessages, new Comparator<Pair<AbstractMessageModel, Integer>>() {
- @Override
- public int compare(Pair<AbstractMessageModel, Integer> lhs, Pair<AbstractMessageModel, Integer> rhs) {
- return lhs.second - rhs.second;
- }
- });
- }
- selectedMessages.clear();
- if (actionMode != null) {
- actionMode.finish();
- }
- try {
- deleteSnackbar = Snackbar.make(coordinatorLayout, deleteableMessagesCount + " " + getString(R.string.message_deleted), 7 * (int) DateUtils.SECOND_IN_MILLIS);
- deleteSnackbar.setAction(R.string.message_delete_undo, v -> RuntimeUtil.runOnUiThread(this::undoDeleteMessages));
- deleteSnackbar.setCallback(new Snackbar.Callback() {
- @Override
- public void onDismissed(Snackbar snackbar, int event) {
- super.onDismissed(snackbar, event);
- if (event != DISMISS_EVENT_ACTION && event != DISMISS_EVENT_CONSECUTIVE && event != DISMISS_EVENT_MANUAL) {
- RuntimeUtil.runOnUiThread(() -> deleteDeleteableMessages());
- }
- }
- });
- deleteSnackbar.show();
- } catch (Exception e) {
- logger.debug("https://issuetracker.google.com/issues/63793040");
- RuntimeUtil.runOnUiThread(this::undoDeleteMessages);
- }
- } else {
- if (actionMode != null) {
- actionMode.finish();
- }
- }
- }
- private void undoDeleteMessages() {
- synchronized (deleteableMessages) {
- for (Pair<AbstractMessageModel, Integer> m : deleteableMessages) {
- composeMessageAdapter.insert(m.first, m.second);
- }
- deleteableMessages.clear();
- }
- composeMessageAdapter.notifyDataSetChanged();
- }
- private synchronized void deleteDeleteableMessages() {
- if (deleteableMessages.size() > 0) {
- synchronized (deleteableMessages) {
- for (Pair<AbstractMessageModel, Integer> m : deleteableMessages) {
- if (m != null) {
- messageService.remove(m.first);
- }
- }
- deleteableMessages.clear();
- if (messageReceiver != null) {
- if (messageReceiver.getMessagesCount() <= 0 && messageReceiver instanceof ContactMessageReceiver) {
- conversationService.clear(messageReceiver);
- } else {
- conversationService.refresh(messageReceiver);
- }
- }
- }
- }
- }
- @UiThread
- private void contactTypingStateChanged(boolean isTyping) {
- RuntimeUtil.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if(isTypingView != null) {
- logger.debug("is typing " + isTyping + " footer view count " + convListView.getFooterViewsCount());
- if (isTyping) {
- //remove if the the another footer element added
- if(convListView.getFooterViewsCount() == 0) {
- isTypingView.setVisibility(View.VISIBLE);
- convListView.addFooterView(isTypingView, null, false);
- }
- } else {
- removeIsTypingFooter();
- }
- }
- }
- });
- }
- private void removeIsTypingFooter() {
- if (isTypingView != null) {
- isTypingView.setVisibility(View.GONE);
- if (convListView != null && convListView.getFooterViewsCount() > 0){
- convListView.removeFooterView(isTypingView);
- }
- }
- }
- @UiThread
- private boolean addMessageToList(AbstractMessageModel message) {
- if (message == null || this.messageReceiver == null || this.composeMessageAdapter == null) {
- return false;
- }
- //check if the message already added
- if (this.listInitializedAt != null && message.getCreatedAt().before(this.listInitializedAt)) {
- return false;
- }
- if (!this.messageReceiver.isMessageBelongsToMe(message)) {
- //do nothing, not my thread
- return false;
- }
- logger.debug("addMessageToList: started");
- this.composeMessageAdapter.removeFirstUnreadPosition();
- // if previous message is from another date, add a date separator
- synchronized (this.messageValues) {
- int size = this.messageValues.size();
- Date date = new Date();
- Date createdAt = size > 0 ? this.messageValues.get(size - 1).getCreatedAt() : new Date(0L);
- if (!dayFormatter.format(createdAt).equals(dayFormatter.format(date))) {
- final DateSeparatorMessageModel dateSeparatorMessageModel = new DateSeparatorMessageModel();
- dateSeparatorMessageModel.setCreatedAt(date);
- this.messageValues.add(size, dateSeparatorMessageModel);
- }
- }
- this.composeMessageAdapter.add(message);
- if (!this.isPaused) {
- new Thread(
- new ReadMessagesRoutine(Arrays.asList(message),
- this.messageService,
- this.notificationService)
- ).start();
- } else {
- this.unreadMessages.add(message);
- }
- if (message.isOutbox()) {
- // scroll to bottom on outgoing message
- scrollList(Integer.MAX_VALUE);
- }
- logger.debug("addMessageToList: finished");
- return true;
- }
- @UiThread
- private void scrollList(final int targetPosition) {
- logger.debug("scrollList " + targetPosition);
- if (this.listUpdateInProgress) {
- logger.debug("Update in progress");
- return;
- }
- if (this.composeMessageAdapter == null) {
- return;
- }
- this.composeMessageAdapter.notifyDataSetChanged();
- this.convListView.post(new Runnable() {
- @Override
- public void run() {
- int topEntry = convListView.getFirstVisiblePosition();
- // update only if really necessary
- if (targetPosition != topEntry) {
- listUpdateInProgress = true;
- int listEntryCount = convListView.getCount();
- if (topEntry > targetPosition) {
- // scroll up
- int startPosition = targetPosition + SMOOTHSCROLL_THRESHOLD;
- if (startPosition < listEntryCount) {
- convListView.setSelection(targetPosition);
- } else {
- convListView.smoothScrollToPosition(targetPosition);
- }
- } else {
- // scroll down
- int startPosition = listEntryCount - SMOOTHSCROLL_THRESHOLD;
- if (listEntryCount - convListView.getLastVisiblePosition() > SMOOTHSCROLL_THRESHOLD && startPosition > 0) {
- convListView.setSelection(targetPosition);
- } else {
- convListView.smoothScrollToPosition(targetPosition);
- }
- }
- listUpdateInProgress = false;
- }
- }
- });
- }
- /**
- * Loading the next records for the listview
- */
- @WorkerThread
- private List<AbstractMessageModel> getNextRecords() {
- List<AbstractMessageModel> messageModels = this.messageService.getMessagesForReceiver(this.messageReceiver, this.nextMessageFilter);
- this.valuesLoaded(messageModels);
- return messageModels;
- }
- @WorkerThread
- private List<AbstractMessageModel> getAllRecords() {
- List<AbstractMessageModel> messageModels = this.messageService.getMessagesForReceiver(this.messageReceiver);
- this.valuesLoaded(messageModels);
- return messageModels;
- }
- /**
- * Append records to the list, adding date separators if necessary
- * Locks list by calling setNotifyOnChange(false) on the adapter to speed up list ctrl
- * Don't forget to call notifyDataSetChanged() on the adapter in the UI thread after inserting
- * @param values MessageModels to insert
- * @param clear Whether previous list entries should be cleared before appending
- * @param markasread Whether chat should be marked as read
- * @return Number of items that have been added to the list INCLUDING date separators and other decoration
- */
- @UiThread
- private int insertToList(final List<AbstractMessageModel> values, boolean clear, boolean markasread, boolean notify) {
- int insertedSize = 0;
- this.composeMessageAdapter.setNotifyOnChange(false);
- synchronized (this.messageValues) {
- int initialSize = this.messageValues.size();
- Date date = new Date();
- if (clear) {
- this.messageValues.clear();
- } else {
- // prevent duplicate date separators when adding messages to an existing chat (e.g. after pull-to-refresh)
- if (this.messageValues.size() > 0) {
- if (this.messageValues.get(0) instanceof DateSeparatorMessageModel) {
- this.messageValues.remove(0);
- }
- AbstractMessageModel topmostMessage = this.messageValues.get(0);
- if (topmostMessage != null) {
- Date topmostDate = topmostMessage.getCreatedAt();
- if (topmostDate != null) {
- date = topmostDate;
- }
- }
- }
- }
- for (AbstractMessageModel m : values) {
- Date createdAt = m.getCreatedAt();
- if (createdAt != null) {
- if (!dayFormatter.format(createdAt).equals(dayFormatter.format(date))) {
- if (!this.messageValues.isEmpty()) {
- final DateSeparatorMessageModel dateSeparatorMessageModel = new DateSeparatorMessageModel();
- dateSeparatorMessageModel.setCreatedAt(this.messageValues.get(0).getCreatedAt());
- this.messageValues.add(0, dateSeparatorMessageModel);
- }
- date = createdAt;
- }
- }
- this.messageValues.add(0, m);
- }
- if (!this.messageValues.isEmpty() && !(this.messageValues.get(0) instanceof DateSeparatorMessageModel)) {
- // add topmost date separator
- final DateSeparatorMessageModel dateSeparatorMessageModel = new DateSeparatorMessageModel();
- dateSeparatorMessageModel.setCreatedAt(this.messageValues.get(0).getCreatedAt());
- this.messageValues.add(0, dateSeparatorMessageModel);
- }
- this.listInitializedAt = new Date();
- insertedSize = this.messageValues.size() - initialSize;
- }
- if (clear) {
- composeMessageAdapter.setNotifyOnChange(true);
- composeMessageAdapter.notifyDataSetInvalidated();
- } else {
- if (notify) {
- composeMessageAdapter.notifyDataSetChanged();
- } else {
- composeMessageAdapter.setNotifyOnChange(true);
- }
- }
- if (markasread) {
- markAsRead();
- }
- return insertedSize;
- }
- private void valuesLoaded(List<AbstractMessageModel> values) {
- if(values != null && values.size() > 0) {
- this.currentPageReferenceId = values.get(values.size()-1).getId();
- }
- }
- /**
- * initialize conversation list and set the unread message count
- * @return number of unread messages
- */
- @SuppressLint({"StaticFieldLeak", "WrongThread"})
- @UiThread
- private void initConversationList(@Nullable Runnable runAfter) {
- this.unreadCount = (int) this.messageReceiver.getUnreadMessagesCount();
- if (this.unreadCount > MESSAGE_PAGE_SIZE) {
- new AsyncTask<Void, Void, List<AbstractMessageModel>>() {
- @Override
- protected void onPreExecute() {
- GenericProgressDialog.newInstance(-1, R.string.please_wait).show(getParentFragmentManager(), DIALOG_TAG_LOADING_MESSAGES);
- }
- @Override
- protected List<AbstractMessageModel> doInBackground(Void... voids) {
- return messageService.getMessagesForReceiver(messageReceiver, new MessageService.MessageFilter() {
- @Override
- public long getPageSize() {
- return -1;
- }
- @Override
- public Integer getPageReferenceId() {
- return null;
- }
- @Override
- public boolean withStatusMessages() {
- return false;
- }
- @Override
- public boolean withUnsaved() {
- return false;
- }
- @Override
- public boolean onlyUnread() {
- return false;
- }
- @Override
- public boolean onlyDownloaded() {
- return false;
- }
- @Override
- public MessageType[] types() {
- return new MessageType[0];
- }
- @Override
- public int[] contentTypes() {
- return null;
- }
- });
- }
- @Override
- protected void onPostExecute(List<AbstractMessageModel> values) {
- valuesLoaded(values);
- populateList(values);
- DialogUtil.dismissDialog(getParentFragmentManager(), DIALOG_TAG_LOADING_MESSAGES, true);
- if (runAfter != null) {
- runAfter.run();
- }
- }
- }.execute();
- } else {
- populateList(getNextRecords());
- if (runAfter != null) {
- runAfter.run();
- }
- }
- }
- /**
- * Populate ListView with provided message models
- * @param values
- */
- @UiThread
- private void populateList(List<AbstractMessageModel> values) {
- if (composeMessageAdapter != null) {
- // re-use existing adapter (for example on tablets)
- composeMessageAdapter.clear();
- composeMessageAdapter.setThumbnailWidth(ConfigUtils.getPreferredThumbnailWidth(getContext(), false));
- composeMessageAdapter.setGroupId(groupId);
- composeMessageAdapter.setMessageReceiver(messageReceiver);
- composeMessageAdapter.setUnreadMessagesCount(unreadCount);
- insertToList(values, true, true, true);
- updateToolbarTitle();
- } else {
- thumbnailCache = new ThumbnailCache<Integer>(null);
- composeMessageAdapter = new ComposeMessageAdapter(
- activity,
- messagePlayerService,
- messageValues,
- userService,
- contactService,
- fileService,
- messageService,
- ballotService,
- preferenceService,
- downloadService,
- licenseService,
- messageReceiver,
- convListView,
- thumbnailCache,
- ConfigUtils.getPreferredThumbnailWidth(getContext(), false),
- ComposeMessageFragment.this,
- unreadCount
- );
- //adding footer before setting the list adapter (android < 4.4)
- if (null != convListView && !isGroupChat && !isDistributionListChat) {
- //create the istyping instance for later use
- isTypingView = layoutInflater.inflate(R.layout.conversation_list_item_typing, null);
- convListView.addFooterView(isTypingView, null, false);
- }
- composeMessageAdapter.setGroupId(groupId);
- composeMessageAdapter.setOnClickListener(new ComposeMessageAdapter.OnClickListener() {
- @Override
- public void resend(AbstractMessageModel messageModel) {
- if (messageModel.isOutbox() && messageModel.getState() == MessageState.SENDFAILED && messageReceiver.isMessageBelongsToMe(messageModel)) {
- try {
- messageService.resendMessage(messageModel, messageReceiver, null);
- } catch (Exception e) {
- RuntimeUtil.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(getContext(), R.string.original_file_no_longer_avilable, Toast.LENGTH_LONG).show();
- }
- });
- }
- }
- }
- @Override
- public void click(View view, int position, AbstractMessageModel messageModel) {
- if (searchActionMode == null) {
- onListItemClick(view, position, messageModel);
- }
- }
- @Override
- public void longClick(View view, int position, AbstractMessageModel messageModel) {
- if (searchActionMode == null) {
- onListItemLongClick(view, position);
- }
- }
- @Override
- public boolean touch(View view, MotionEvent motionEvent, AbstractMessageModel messageModel) {
- if (listViewSwipeListener != null && searchActionMode == null) {
- return listViewSwipeListener.onTouch(view, motionEvent);
- }
- return false;
- }
- @Override
- public void avatarClick(View view, int position, AbstractMessageModel messageModel) {
- if (messageModel != null && messageModel.getIdentity() != null) {
- ContactModel contactModel = contactService.getByIdentity(messageModel.getIdentity());
- if (contactModel != null) {
- Intent intent;
- if (messageModel instanceof GroupMessageModel || messageModel instanceof DistributionListMessageModel) {
- intent = new Intent(getActivity(), ComposeMessageActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
- intent.setData((Uri.parse("foobar://" + SystemClock.elapsedRealtime())));
- IntentDataUtil.append(contactModel, intent);
- getActivity().finish();
- } else {
- intent = new Intent(getActivity(), ContactDetailActivity.class);
- intent.putExtra(ThreemaApplication.INTENT_DATA_CONTACT_READONLY, true);
- IntentDataUtil.append(contactModel, intent);
- }
- AnimationUtil.startActivityForResult(getActivity(), view, intent, 0);
- }
- }
- }
- @SuppressLint("DefaultLocale")
- @Override
- public void onSearchResultsUpdate(int searchResultsIndex, int searchResultsSize) {
- RuntimeUtil.runOnUiThread(() -> {
- if (searchCounter != null) {
- try {
- searchCounter.setText(String.format("%d / %d", searchResultsIndex, searchResultsSize));
- } catch (Exception e) {
- //
- }
- }
- });
- }
- @Override
- public void onSearchInProgress(boolean inProgress) {
- RuntimeUtil.runOnUiThread(() -> {
- if (searchNextButton != null && searchPreviousButton != null) {
- try {
- searchPreviousButton.setVisibility(inProgress ? View.INVISIBLE : View.VISIBLE);
- searchNextButton.setVisibility(inProgress ? View.INVISIBLE : View.VISIBLE);
- searchProgress.setVisibility(inProgress ? View.VISIBLE : View.INVISIBLE);
- } catch (Exception e) {
- //
- }
- }
- });
- }
- });
- insertToList(values, false, !hiddenChatsListService.has(messageReceiver.getUniqueIdString()), false);
- convListView.setAdapter(composeMessageAdapter);
- convListView.setItemsCanFocus(false);
- convListView.setVisibility(View.VISIBLE);
- }
- setIdentityColors();
- removeIsTypingFooter();
- }
- /**
- * Jump to first unread message keeping in account shift caused by date separators and other decorations
- * Currently depends on various globals...
- */
- @UiThread
- private void jumpToFirstUnreadMessage() {
- if (unreadCount > 0) {
- synchronized (this.messageValues) {
- int position = Math.min(convListView.getCount() - unreadCount, this.messageValues.size() - 1);
- while (position >= 0) {
- if (this.messageValues.get(position) instanceof FirstUnreadMessageModel) {
- break;
- }
- position--;
- }
- unreadCount = 0;
- if (position > 0) {
- final int finalPosition = position;
- logger.debug("jump to initial position " + finalPosition);
- convListView.setSelection(finalPosition);
- convListView.postDelayed(() -> {
- convListView.setSelection(finalPosition);
- }, 750);
- return;
- }
- }
- }
- convListView.setSelection(Integer.MAX_VALUE);
- }
- private void setIdentityColors() {
- logger.debug("setIdentityColors");
- if (this.isGroupChat) {
- Map<String, Integer> colors = this.groupService.getGroupMemberColors(this.groupModel);
- if (ConfigUtils.getAppTheme(activity) == ConfigUtils.THEME_DARK) {
- Map<String, Integer> darkColors = new HashMap<>();
- @ColorInt final int bubbleColorRecv = activity.getResources().getColor(R.color.dark_bubble_receive);
- // lighten up some colors to ensure better visibility if dark theme is enabled
- for (Map.Entry<String, Integer> entry : colors.entrySet()) {
- @ColorInt int newColor;
- try {
- newColor = entry.getValue();
- if (ColorUtils.calculateContrast(newColor, bubbleColorRecv) <= 1.7) {
- float[] hsl = new float[3];
- ColorUtils.colorToHSL(entry.getValue(), hsl);
- if (hsl[2] < 0.7f) {
- hsl[2] = 0.7f; // pull up luminance
- }
- if (hsl[1] > 0.6f) {
- hsl[1] = 0.6f; // tone down saturation
- }
- newColor = ColorUtils.HSLToColor(hsl);
- }
- darkColors.put(entry.getKey(), newColor);
- } catch (Exception e) {
- //
- }
- }
- this.identityColors = darkColors;
- } else {
- this.identityColors = colors;
- }
- } else {
- if (this.identityColors != null) {
- this.identityColors.clear();
- }
- }
- this.composeMessageAdapter.setIdentityColors(this.identityColors);
- }
- private void onListItemClick(View view, int position, AbstractMessageModel messageModel) {
- if (view == null) {
- return;
- }
- if (actionMode != null) {
- if (selectedMessages.contains(messageModel)) {
- // remove from selection
- selectedMessages.remove(messageModel);
- convListView.setItemChecked(position, false);
- } else {
- if (convListView.getCheckedItemCount() < MAX_SELECTED_ITEMS) {
- // add this to selection
- selectedMessages.add(messageModel);
- convListView.setItemChecked(position, true);
- } else {
- convListView.setItemChecked(position, false);
- }
- }
- final int checked = convListView.getCheckedItemCount();
- if (checked > 0) {
- // invalidate menu to update display => onPrepareActionMode()
- actionMode.invalidate();
- } else {
- actionMode.finish();
- }
- } else {
- if (view.isSelected()) {
- view.setSelected(false);
- }
- if (convListView.isItemChecked(position)) {
- convListView.setItemChecked(position, false);
- }
- // check if item is a quote
- if (QuoteUtil.isQuoteV1(messageModel.getBody())) {
- QuoteUtil.QuoteContent quoteContent = QuoteUtil.getQuoteContent(
- messageModel,
- messageReceiver.getType(),
- false,
- thumbnailCache,
- getContext(),
- this.messageService,
- this.userService,
- this.fileService
- );
- if (quoteContent != null) {
- if (searchActionMode != null) {
- searchActionMode.finish();
- }
- ComposeMessageAdapter.ConversationListFilter filter = (ComposeMessageAdapter.ConversationListFilter) composeMessageAdapter.getQuoteFilter(quoteContent);
- // search for quoted text
- filter.filter(quoteContent.quotedText, count -> {
- if (count == 0) {
- SingleToast.getInstance().showShortText(getString(R.string.quote_not_found));
- }
- });
- }
- } else if (messageModel.getQuotedMessageId() != null) {
- QuoteUtil.QuoteContent quoteContent = QuoteUtil.getQuoteContent(
- messageModel,
- messageReceiver.getType(),
- false,
- thumbnailCache,
- getContext(),
- this.messageService,
- this.userService,
- this.fileService
- );
- if (quoteContent != null) {
- if (searchActionMode != null) {
- searchActionMode.finish();
- }
- AbstractMessageModel quotedMessageModel = messageService.getMessageModelByApiMessageId(messageModel.getQuotedMessageId(), messageReceiver.getType());
- if (quotedMessageModel != null) {
- ComposeMessageAdapter.ConversationListFilter filter = (ComposeMessageAdapter.ConversationListFilter) composeMessageAdapter.getQuoteFilter(quoteContent);
- searchV2Quote(quotedMessageModel.getApiMessageId(), filter);
- } else {
- Toast.makeText(getContext().getApplicationContext(), R.string.quoted_message_deleted, Toast.LENGTH_SHORT).show();
- }
- }
- }
- }
- }
- /**
- * Recursively search for message with provided apiMessageId in chat and gradually load more records to Adapter until matching message is found by provided Filter
- * TODO: we should provide a static version of this that does not rely on globals
- * @param apiMessageId to search for
- * @param filter Filter to use for this search
- */
- @UiThread
- synchronized private void searchV2Quote(final String apiMessageId, final ComposeMessageAdapter.ConversationListFilter filter) {
- filter.filter("#" + apiMessageId, new Filter.FilterListener() {
- @SuppressLint("StaticFieldLeak")
- @Override
- public void onFilterComplete(int count) {
- if (count == 0) {
- new AsyncTask<Void, Void, Integer>() {
- List<AbstractMessageModel> messageModels;
- @Override
- protected Integer doInBackground(Void... params) {
- messageModels = getNextRecords();
- if (messageModels != null) {
- return messageModels.size();
- }
- return null;
- }
- @Override
- protected void onPostExecute(Integer result) {
- if (getContext() != null) {
- if (result != null && result > 0) {
- insertToList(messageModels, false, false, true);
- if (getFragmentManager() != null) {
- if (getFragmentManager().findFragmentByTag(DIALOG_TAG_SEARCHING) == null) {
- GenericProgressDialog.newInstance(R.string.searching, R.string.please_wait).show(getFragmentManager(), DIALOG_TAG_SEARCHING);
- }
- searchV2Quote(apiMessageId, filter);
- }
- } else {
- SingleToast.getInstance().showShortText(getString(R.string.quote_not_found));
- swipeRefreshLayout.setEnabled(false);
- }
- }
- }
- }.execute();
- } else {
- DialogUtil.dismissDialog(getFragmentManager(), DIALOG_TAG_SEARCHING, true);
- }
- }
- });
- }
- @UiThread
- private void onListItemLongClick(@NonNull View view, final int position) {
- int viewType = composeMessageAdapter.getItemViewType(position);
- if (viewType == ComposeMessageAdapter.TYPE_FIRST_UNREAD || viewType == ComposeMessageAdapter.TYPE_DATE_SEPARATOR) {
- return;
- }
- selectedMessages.clear();
- selectedMessages.add(composeMessageAdapter.getItem(position));
- if (actionMode != null) {
- convListView.clearChoices();
- convListView.setItemChecked(position, true);
- actionMode.invalidate();
- } else {
- convListView.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE);
- convListView.setItemChecked(position, true);
- view.setSelected(true);
- actionMode = activity.startSupportActionMode(new ComposeMessageAction(position));
- }
- view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
- // fix linkify on longclick problem
- // see: http://stackoverflow.com/questions/16047215/android-how-to-stop-linkify-on-long-press
- longClickItem = position;
- }
- private boolean isMuted() {
- if (messageReceiver != null && mutedChatsListService != null) {
- String uniqueId = messageReceiver.getUniqueIdString();
- return !TestUtil.empty(uniqueId) && mutedChatsListService.has(uniqueId);
- }
- return false;
- }
- private boolean isMentionsOnly() {
- if (messageReceiver != null && mentionOnlyChatsListService != null) {
- String uniqueId = messageReceiver.getUniqueIdString();
- return !TestUtil.empty(uniqueId) && mentionOnlyChatsListService.has(uniqueId);
- }
- return false;
- }
- private boolean isSilent() {
- if (messageReceiver != null && ringtoneService != null) {
- String uniqueId = messageReceiver.getUniqueIdString();
- return !TestUtil.empty(uniqueId) && ringtoneService.hasCustomRingtone(uniqueId) && ringtoneService.isSilent(uniqueId, isGroupChat);
- }
- return false;
- }
- private void playInAppSound(final int resId, final boolean isVibrate) {
- if (this.isMuted() || this.isSilent()) {
- //do not play
- return;
- }
- RuntimeUtil.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- int ringerMode = audioManager.getRingerMode();
- boolean isSilent = (ringerMode == AudioManager.RINGER_MODE_SILENT
- || ringerMode == AudioManager.RINGER_MODE_VIBRATE);
- if (preferenceService.isInAppSounds() && !isSilent) {
- MediaPlayerStateWrapper mediaPlayer = new MediaPlayerStateWrapper();
- mediaPlayer.setAudioAttributes(new AudioAttributes.Builder()
- .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
- .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
- .build());
- mediaPlayer.setVolume(0.3f, 0.3f);
- mediaPlayer.setStateListener(new MediaPlayerStateWrapper.StateListener() {
- @Override
- public void onCompletion(MediaPlayer mp) {
- if (mp.isPlaying()) {
- mp.stop();
- }
- mp.reset();
- mp.release();
- }
- @Override
- public void onPrepared(MediaPlayer mp) {}
- });
- try (AssetFileDescriptor afd = ComposeMessageFragment.this.getResources().openRawResourceFd(resId)) {
- mediaPlayer.setDataSource(afd);
- mediaPlayer.prepare();
- mediaPlayer.start();
- } catch (Exception e) {
- logger.debug("could not play in-app sound.");
- mediaPlayer.release();
- }
- }
- if (preferenceService.isInAppVibrate() && isVibrate) {
- Vibrator vibrator = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE);
- if (vibrator != null) {
- switch (ringerMode) {
- case AudioManager.RINGER_MODE_VIBRATE:
- case AudioManager.RINGER_MODE_NORMAL:
- vibrator.vibrate(VIBRATION_MSEC);
- break;
- default:
- break;
- }
- }
- }
- }
- });
- }
- private void playSentSound() {
- playInAppSound(R.raw.sent_message, false);
- }
- private void playReceivedSound() {
- playInAppSound(R.raw.received_message, true);
- }
- private void sendTextMessage() {
- if (!this.validateSendingPermission()) {
- return;
- }
- if (!TestUtil.empty(this.messageText.getText())) {
- final CharSequence message;
- if (isQuotePanelShown()) {
- message = QuoteUtil.quote(
- this.messageText.getText().toString(),
- quoteInfo.quoteIdentity,
- quoteInfo.quoteText,
- quoteInfo.messageModel
- );
- closeQuoteMode();
- } else {
- message = this.messageText.getText();
- }
- if (!TestUtil.empty(message)) {
- // block send button to avoid double posting
- this.messageText.setText("");
- if (typingIndicatorTextWatcher != null) {
- messageText.removeTextChangedListener(typingIndicatorTextWatcher);
- }
- if (typingIndicatorTextWatcher != null) {
- messageText.addTextChangedListener(typingIndicatorTextWatcher);
- }
- //send stopped typing message
- if (typingIndicatorTextWatcher != null) {
- typingIndicatorTextWatcher.stopTyping();
- }
- new Thread(new Runnable() {
- @Override
- public void run() {
- TextMessageSendAction.getInstance()
- .sendTextMessage(new MessageReceiver[]{messageReceiver}, message.toString(), new SendAction.ActionHandler() {
- @Override
- public void onError(final String errorMessage) {
- RuntimeUtil.runOnUiThread(() -> {
- Toast.makeText(getActivity(), errorMessage, Toast.LENGTH_LONG).show();
- if (!TestUtil.empty(message)) {
- messageText.setText(message);
- messageText.setSelection(messageText.length());
- }
- });
- }
- @Override
- public void onWarning(String warning, boolean continueAction) {
- }
- @Override
- public void onProgress(final int progress, final int total) {
- }
- @Override
- public void onCompleted() {
- RuntimeUtil.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- scrollList(Integer.MAX_VALUE);
- if (ConfigUtils.isTabletLayout()) {
- // remove draft right now to make sure conversations pane is updated
- ThreemaApplication.putMessageDraft(messageReceiver.getUniqueIdString(), "");
- }
- }
- });
- }
- });
- }
- }).start();
- }
- } else {
- if (ConfigUtils.requestAudioPermissions(getActivity(), this, PERMISSION_REQUEST_ATTACH_VOICE_MESSAGE)) {
- attachVoiceMessage();
- }
- }
- }
- private void attachVoiceMessage() {
- closeQuoteMode();
- // stop all message players
- if (this.messagePlayerService != null) {
- this.messagePlayerService.pauseAll(SOURCE_AUDIORECORDER);
- }
- Intent intent = new Intent(activity, VoiceRecorderActivity.class);
- IntentDataUtil.addMessageReceiverToIntent(intent, messageReceiver);
- activity.startActivityForResult(intent, ACTIVITY_ID_VOICE_RECORDER);
- activity.overridePendingTransition(R.anim.slide_in_left_short, 0);
- }
- private void copySelectedMessagesToClipboard() {
- AbstractMessageModel messageModel = selectedMessages.get(0);
- if (messageModel == null) {
- logger.error("no message model", activity);
- return;
- }
- String body = "";
- for (AbstractMessageModel message : selectedMessages) {
- if (body.length() > 0) {
- body += "\n";
- }
- body += message.getType() == MessageType.TEXT ?
- QuoteUtil.getMessageBody(message, false) :
- message.getCaption();
- }
- try {
- ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
- if (clipboard != null) {
- ClipData clipData = ClipData.newPlainText(null, body);
- if (clipData != null) {
- clipboard.setPrimaryClip(clipData);
- Snackbar.make(coordinatorLayout, R.string.message_copied, Snackbar.LENGTH_SHORT).show();
- }
- }
- } catch (Exception e) {
- // Some Android 4.3 devices raise an IllegalStateException when writing to the clipboard
- // while there is an active clipboard listener
- // see https://code.google.com/p/android/issues/detail?id=58043
- logger.error("Exception", e);
- }
- }
- @SuppressLint("StaticFieldLeak")
- private void shareMessages() {
- if (selectedMessages.size() > 1) {
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected void onPreExecute() {
- GenericProgressDialog.newInstance(R.string.decoding_message, R.string.please_wait).show(getFragmentManager(), DIALOG_TAG_DECRYPTING_MESSAGES);
- }
- @Override
- protected Void doInBackground(Void... voids) {
- fileService.loadDecryptedMessageFiles(selectedMessages, new FileService.OnDecryptedFilesComplete() {
- @Override
- public void complete(ArrayList<Uri> uris) {
- shareMediaMessages(uris);
- }
- @Override
- public void error(String message) {
- RuntimeUtil.runOnUiThread(() -> Toast.makeText(activity, message, Toast.LENGTH_LONG).show());
- }
- });
- return null;
- }
- @Override
- protected void onPostExecute(Void aVoid) {
- DialogUtil.dismissDialog(getFragmentManager(), DIALOG_TAG_DECRYPTING_MESSAGES, true);
- }
- }.execute();
- } else {
- final AbstractMessageModel messageModel = selectedMessages.get(0);
- if (messageModel != null) {
- fileService.loadDecryptedMessageFile(messageModel, new FileService.OnDecryptedFileComplete() {
- @Override
- public void complete(File decryptedFile) {
- if (decryptedFile != null) {
- String filename = null;
- if (messageModel.getType() == MessageType.FILE) {
- filename = messageModel.getFileData().getFileName();
- }
- shareMediaMessages(Collections.singletonList(fileService.getShareFileUri(decryptedFile, filename)));
- } else {
- messageService.shareTextMessage(activity, messageModel);
- }
- }
- @Override
- public void error(final String message) {
- RuntimeUtil.runOnUiThread(() -> Toast.makeText(activity, message, Toast.LENGTH_LONG).show());
- }
- });
- }
- }
- }
- private void shareMediaMessages(List<Uri> uris) {
- if (selectedMessages.size() == 1) {
- ExpandableTextEntryDialog alertDialog = ExpandableTextEntryDialog.newInstance(
- getString(R.string.share_media),
- R.string.add_caption_hint, selectedMessages.get(0).getCaption(),
- R.string.label_continue, R.string.cancel, true);
- alertDialog.setData(uris);
- alertDialog.setTargetFragment(this, 0);
- alertDialog.show(getParentFragmentManager(), null);
- } else {
- messageService.shareMediaMessages(activity,
- new ArrayList<>(selectedMessages),
- new ArrayList<>(uris), null);
- }
- }
- @Override
- public void onYes(String tag, Object data, String text) {
- List<Uri> uris = (List<Uri>) data;
- messageService.shareMediaMessages(activity,
- new ArrayList<>(selectedMessages),
- new ArrayList<>(uris), text);
- }
- @Override
- public void onContactSelected(String identity, int length, int insertPosition) {
- Editable editable = this.messageText.getText();
- if (insertPosition >= 0 && insertPosition <= editable.length()) {
- editable.delete(insertPosition, insertPosition + length);
- this.messageText.addMention(identity);
- }
- }
- private void startQuoteMode(AbstractMessageModel messageModel, Runnable onFinishRunnable) {
- if (messageModel == null) {
- messageModel = selectedMessages.get(0);
- }
- String body = QuoteUtil.getMessageBody(messageModel, true);
- if (!TestUtil.empty(body) || ConfigUtils.canCreateV2Quotes()) {
- sendButton.setEnabled(messageText != null && !TestUtil.empty(messageText.getText()));
- quoteInfo.quoteIdentity = messageModel.isOutbox() ? userService.getIdentity() : messageModel.getIdentity();
- quoteInfo.quoteIdentityView.setText(
- NameUtil.getQuoteName(quoteInfo.quoteIdentity, this.contactService, this.userService)
- );
- int color = ConfigUtils.getAccentColor(activity);
- if (!messageModel.isOutbox()) {
- if (isGroupChat) {
- if (identityColors != null && identityColors.containsKey(quoteInfo.quoteIdentity)) {
- color = identityColors.get(quoteInfo.quoteIdentity);
- }
- } else {
- if (contactModel != null) {
- color = contactModel.getColor();
- }
- }
- }
- quoteInfo.quoteBar.setBackgroundColor(color);
- quoteInfo.quoteTextView.setText(emojiMarkupUtil.addTextSpans(activity, body, quoteInfo.quoteTextView, false, false));
- quoteInfo.quoteText = body;
- quoteInfo.messageModel = messageModel;
- quoteInfo.quoteThumbnail.setVisibility(View.GONE);
- quoteInfo.quoteTypeImage.setVisibility(View.GONE);
- if (ConfigUtils.canCreateV2Quotes()) {
- try {
- Bitmap thumbnail = fileService.getMessageThumbnailBitmap(messageModel, thumbnailCache);
- if (thumbnail != null) {
- quoteInfo.quoteThumbnail.setImageBitmap(thumbnail);
- quoteInfo.quoteThumbnail.setVisibility(View.VISIBLE);
- }
- } catch (Exception ignore) {
- }
- MessageUtil.MessageViewElement messageViewElement = MessageUtil.getViewElement(getContext(), messageModel);
- if (messageViewElement.icon != null) {
- quoteInfo.quoteTypeImage.setImageResource(messageViewElement.icon);
- quoteInfo.quoteTypeImage.setVisibility(View.VISIBLE);
- }
- }
- AnimationUtil.expand(quoteInfo.quotePanel, onFinishRunnable);
- }
- }
- private void closeQuoteMode() {
- quoteInfo.quoteIdentityView.setText("");
- quoteInfo.quoteTextView.setText("");
- if (isQuotePanelShown()) {
- AnimationUtil.collapse(quoteInfo.quotePanel, () -> updateSendButton(messageText.getText()));
- }
- }
- private boolean isQuotePanelShown() {
- return quoteInfo.quotePanel != null && quoteInfo.quotePanel.getVisibility() == View.VISIBLE;
- }
- private void startForwardMessage() {
- if (selectedMessages.size() > 0) {
- if (selectedMessages.size() == 1) {
- final AbstractMessageModel messageModel = selectedMessages.get(0);
- if (messageModel.getType() == MessageType.TEXT) {
- // allow editing before sending if it's a single text message
- String body = QuoteUtil.getMessageBody(messageModel, false);
- Intent intent = new Intent(activity, RecipientListBaseActivity.class);
- intent.setType("text/plain");
- intent.setAction(Intent.ACTION_SEND);
- intent.putExtra(Intent.EXTRA_TEXT, body);
- intent.putExtra(ThreemaApplication.INTENT_DATA_IS_FORWARD, true);
- activity.startActivity(intent);
- return;
- }
- }
- FileUtil.forwardMessages(activity, RecipientListBaseActivity.class, selectedMessages);
- }
- }
- private void showMessageLog() {
- AbstractMessageModel messageModel = selectedMessages.get(0);
- if (messageModel == null) {
- return;
- }
- MessageDetailDialog.newInstance(R.string.message_log_title, messageModel.getId(), messageModel.getClass().toString()).
- show(getFragmentManager(), "messageLog");
- }
- private void updateToolbarTitle() {
- if (!TestUtil.required(
- this.actionBar,
- this.actionBarSubtitleImageView,
- this.actionBarSubtitleTextView,
- this.actionBarTitleTextView,
- this.emojiMarkupUtil,
- this.messageReceiver) || !requiredInstances()) {
- return;
- }
- this.actionBarSubtitleTextView.setVisibility(View.GONE);
- this.actionBarSubtitleImageView.setVisibility(View.GONE);
- this.actionBarTitleTextView.setText(this.messageReceiver.getDisplayName());
- this.actionBarTitleTextView.setPaintFlags(this.actionBarTitleTextView.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG);
- if (this.isGroupChat) {
- if (!groupService.isGroupMember(this.groupModel)) {
- this.actionBarTitleTextView.setPaintFlags(this.actionBarTitleTextView.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
- }
- actionBarSubtitleTextView.setText(groupService.getMembersString(groupModel));
- actionBarSubtitleTextView.setVisibility(View.VISIBLE);
- actionBarAvatarView.setImageBitmap(groupService.getAvatar(groupModel, false));
- actionBarAvatarView.setBadgeVisible(false);
- } else if (this.isDistributionListChat) {
- actionBarSubtitleTextView.setText(this.distributionListService.getMembersString(this.distributionListModel));
- actionBarSubtitleTextView.setVisibility(View.VISIBLE);
- actionBarAvatarView.setImageBitmap(distributionListService.getAvatar(distributionListModel, false));
- actionBarAvatarView.setBadgeVisible(false);
- } else {
- if (contactModel != null) {
- this.actionBarSubtitleImageView.setContactModel(contactModel);
- this.actionBarSubtitleImageView.setVisibility(View.VISIBLE);
- this.actionBarAvatarView.setImageBitmap(contactService.getAvatar(contactModel, false, true));
- this.actionBarAvatarView.setBadgeVisible(contactService.showBadge(contactModel));
- }
- }
- this.actionBarTitleTextView.invalidate();
- this.actionBarSubtitleTextView.invalidate();
- this.actionBarSubtitleImageView.invalidate();
- }
- @Override
- public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
- inflater.inflate(R.menu.fragment_compose_message, menu);
- this.setupToolbar();
- super.onCreateOptionsMenu(menu, inflater);
- ConfigUtils.addIconsToOverflowMenu(getContext(), menu);
- }
- @Override
- public void onPrepareOptionsMenu(Menu menu) {
- this.callItem = menu.findItem(R.id.menu_threema_call);
- this.deleteDistributionListItem = menu.findItem(R.id.menu_delete_distribution_list);
- this.shortCutItem = menu.findItem(R.id.menu_shortcut);
- this.mutedMenuItem = menu.findItem(R.id.menu_muted);
- this.blockMenuItem = menu.findItem(R.id.menu_block_contact);
- this.showOpenBallotWindowMenuItem = menu.findItem(R.id.menu_ballot_window_show);
- this.showBallotsMenuItem = menu.findItem(R.id.menu_ballot_show_all);
- this.showOpenGroupRequestsMenuItem = menu.findItem(R.id.menu_group_requests_show);
- this.showAllGroupRequestsMenuItem = menu.findItem(R.id.menu_group_request_show_all);
- // initialize menus
- updateMenus();
- // initialize various toolbar items
- this.updateToolbarTitle();
- }
- @SuppressLint("StaticFieldLeak")
- private void updateMenus() {
- logger.debug("updateMenus");
- if (!TestUtil.required(
- this.callItem,
- this.deleteDistributionListItem,
- this.shortCutItem,
- this.mutedMenuItem,
- this.blockMenuItem,
- this.showOpenBallotWindowMenuItem,
- this.showAllGroupRequestsMenuItem,
- this.showOpenGroupRequestsMenuItem,
- isAdded()
- )) {
- return;
- }
- this.deleteDistributionListItem.setVisible(this.isDistributionListChat);
- this.shortCutItem.setVisible(ShortcutManagerCompat.isRequestPinShortcutSupported(getAppContext()));
- this.mutedMenuItem.setVisible(!this.isDistributionListChat && !(isGroupChat && groupService.isNotesGroup(groupModel)));
- updateMuteMenu();
- if (contactModel != null) {
- this.blockMenuItem.setVisible(true);
- updateBlockMenu();
- contactTypingStateChanged(contactService.isTyping(contactModel.getIdentity()));
- } else {
- this.blockMenuItem.setVisible(false);
- }
- new AsyncTask<Void, Void, Long>() {
- @Override
- protected Long doInBackground(Void... voids) {
- return ballotService.countBallots(new BallotService.BallotFilter() {
- @Override
- public MessageReceiver getReceiver() { return messageReceiver; }
- @Override
- public BallotModel.State[] getStates() { return new BallotModel.State[]{BallotModel.State.OPEN}; }
- @Override
- public String createdOrNotVotedByIdentity() {
- return userService.getIdentity();
- }
- @Override
- public boolean filter(BallotModel ballotModel) { return true; }
- });
- }
- @Override
- protected void onPostExecute(Long openBallots) {
- showOpenBallotWindowMenuItem.setVisible(openBallots > 0L);
- if (preferenceService.getBallotOverviewHidden()) {
- showOpenBallotWindowMenuItem.setIcon(R.drawable.ic_outline_visibility);
- showOpenBallotWindowMenuItem.setTitle(R.string.ballot_window_show);
- } else {
- showOpenBallotWindowMenuItem.setIcon(R.drawable.ic_outline_visibility_off);
- showOpenBallotWindowMenuItem.setTitle(R.string.ballot_window_hide);
- }
- Context context = getContext();
- if (context != null) {
- ConfigUtils.themeMenuItem(showOpenBallotWindowMenuItem, ConfigUtils.getColorFromAttribute(context, R.attr.textColorSecondary));
- }
- }
- }.execute();
- new AsyncTask<Void, Void, Long>() {
- @Override
- protected Long doInBackground(Void... voids) {
- return ballotService.countBallots(new BallotService.BallotFilter() {
- @Override
- public MessageReceiver getReceiver() { return messageReceiver; }
- @Override
- public BallotModel.State[] getStates() { return new BallotModel.State[]{BallotModel.State.OPEN, BallotModel.State.CLOSED}; }
- @Override
- public boolean filter(BallotModel ballotModel) { return true; }
- });
- }
- @Override
- protected void onPostExecute(Long hasBallots) {
- showBallotsMenuItem.setVisible(hasBallots > 0L);
- }
- }.execute();
- // show/hide open group request chips if there are any
- if (ConfigUtils.supportsGroupLinks() && !databaseServiceNew.getIncomingGroupJoinRequestModelFactory()
- .getDestictOpenRequestsForGroup(groupId).isEmpty()) {
- showOpenGroupRequestsMenuItem.setVisible(true);
- showAllGroupRequestsMenuItem.setVisible(true);
- if (preferenceService.getGroupRequestsOverviewHidden()) {
- showOpenGroupRequestsMenuItem.setIcon(R.drawable.ic_outline_visibility);
- showOpenGroupRequestsMenuItem.setTitle(R.string.open_group_requests_show);
- } else {
- showOpenGroupRequestsMenuItem.setIcon(R.drawable.ic_outline_visibility_off);
- showOpenGroupRequestsMenuItem.setTitle(R.string.open_group_requests_hide);
- }
- }
- else {
- showOpenGroupRequestsMenuItem.setVisible(false);
- }
- // link to incoming group requests overview if there are any (includes already answered ones)
- showAllGroupRequestsMenuItem.setVisible(ConfigUtils.supportsGroupLinks() &&
- !databaseServiceNew.getIncomingGroupJoinRequestModelFactory()
- .getAllRequestsForGroup(groupId).isEmpty()
- );
- updateVoipCallMenuItem(null);
- }
- private void updateMuteMenu() {
- if (!isAdded() || this.mutedMenuItem == null) {
- // do not update if no longer attached to activity
- return;
- }
- if (isMuted()) {
- this.mutedMenuItem.setIcon(R.drawable.ic_dnd_total_silence_grey600_24dp);
- this.mutedMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
- } else if (isMentionsOnly()) {
- this.mutedMenuItem.setIcon(R.drawable.ic_dnd_mention_grey600_24dp);
- this.mutedMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
- } else if (isSilent()) {
- this.mutedMenuItem.setIcon(R.drawable.ic_notifications_off_outline);
- this.mutedMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
- } else {
- this.mutedMenuItem.setIcon(R.drawable.ic_notifications_active_outline);
- this.mutedMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
- }
- }
- private void updateBlockMenu() {
- if (!isAdded()) {
- // do not update if no longer attached to activity
- return;
- }
- if (TestUtil.required(this.blockMenuItem, this.blackListIdentityService, this.contactModel)) {
- boolean state = this.blackListIdentityService.has(this.contactModel.getIdentity());
- this.blockMenuItem.setTitle(state ? getString(R.string.unblock_contact) : getString(R.string.block_contact));
- this.blockMenuItem.setShowAsAction(state ? MenuItem.SHOW_AS_ACTION_ALWAYS : MenuItem.SHOW_AS_ACTION_NEVER);
- this.mutedMenuItem.setShowAsAction(state ? MenuItem.SHOW_AS_ACTION_NEVER : MenuItem.SHOW_AS_ACTION_IF_ROOM);
- this.mutedMenuItem.setVisible(!state);
- this.callItem.setShowAsAction(state ? MenuItem.SHOW_AS_ACTION_NEVER : MenuItem.SHOW_AS_ACTION_ALWAYS);
- updateVoipCallMenuItem(!state);
- }
- }
- @AnyThread
- private void updateVoipCallMenuItem(final Boolean newState) {
- RuntimeUtil.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if (callItem != null) {
- if (ContactUtil.canReceiveVoipMessages(contactModel, blackListIdentityService)
- && ConfigUtils.isCallsEnabled(getContext(), preferenceService, licenseService)) {
- logger.debug("updateVoipMenu newState " + newState);
- callItem.setVisible(newState != null ? newState : voipStateService.getCallState().isIdle());
- } else {
- callItem.setVisible(false);
- }
- }
- }
- });
- }
- private Intent addExtrasToIntent(Intent intent, MessageReceiver receiver) {
- switch (receiver.getType()) {
- case MessageReceiver.Type_GROUP:
- intent.putExtra(ThreemaApplication.INTENT_DATA_GROUP, groupId);
- break;
- case MessageReceiver.Type_DISTRIBUTION_LIST:
- intent.putExtra(ThreemaApplication.INTENT_DATA_DISTRIBUTION_LIST, distributionListModel.getId());
- break;
- case MessageReceiver.Type_CONTACT:
- default:
- intent.putExtra(ThreemaApplication.INTENT_DATA_CONTACT, identity);
- }
- return intent;
- }
- private void attachCamera() {
- Intent previewIntent = IntentDataUtil.addMessageReceiversToIntent(new Intent(activity, SendMediaActivity.class), new MessageReceiver[]{this.messageReceiver});
- if (this.actionBarTitleTextView != null && this.actionBarTitleTextView.getText() != null) {
- previewIntent.putExtra(ThreemaApplication.INTENT_DATA_TEXT, this.actionBarTitleTextView.getText().toString());
- }
- previewIntent.putExtra(ThreemaApplication.INTENT_DATA_PICK_FROM_CAMERA, true);
- AnimationUtil.startActivityForResult(activity, null, previewIntent, ThreemaActivity.ACTIVITY_ID_SEND_MEDIA);
- }
- private void showPermissionRationale(int stringResource) {
- ConfigUtils.showPermissionRationale(getContext(), coordinatorLayout, stringResource);
- }
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- NavigationUtil.navigateUpToHome(activity);
- break;
- case R.id.menu_search_messages:
- searchActionMode = activity.startSupportActionMode(new SearchActionMode());
- break;
- case R.id.menu_gallery:
- Intent mediaGalleryIntent = new Intent(activity, MediaGalleryActivity.class);
- activity.startActivity(addExtrasToIntent(mediaGalleryIntent, this.messageReceiver));
- break;
- case R.id.menu_threema_call:
- VoipUtil.initiateCall(activity, contactModel, false, null);
- break;
- case R.id.menu_wallpaper:
- wallpaperService.selectWallpaper(this, this.messageReceiver, new Runnable() {
- @Override
- public void run() {
- RuntimeUtil.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- setBackgroundWallpaper();
- }
- });
- }
- });
- activity.overridePendingTransition(R.anim.fast_fade_in, R.anim.fast_fade_out);
- break;
- case R.id.menu_muted:
- if (!isDistributionListChat) {
- Intent intent;
- int[] location = new int[2];
- if (isGroupChat) {
- intent = new Intent(activity, GroupNotificationsActivity.class);
- intent.putExtra(ThreemaApplication.INTENT_DATA_GROUP, this.groupId);
- } else {
- intent = new Intent(activity, ContactNotificationsActivity.class);
- intent.putExtra(ThreemaApplication.INTENT_DATA_CONTACT, this.identity);
- }
- if (ToolbarUtil.getMenuItemCenterPosition(((ThreemaToolbarActivity)activity).getToolbar(), R.id.menu_muted, location)) {
- intent.putExtra((ThreemaApplication.INTENT_DATA_ANIM_CENTER), location);
- }
- activity.startActivity(intent);
- }
- break;
- case R.id.menu_block_contact:
- if (this.blackListIdentityService.has(contactModel.getIdentity())) {
- this.blackListIdentityService.toggle(activity, contactModel);
- updateBlockMenu();
- } else {
- GenericAlertDialog.newInstance(R.string.block_contact, R.string.really_block_contact, R.string.yes, R.string.no).setTargetFragment(this).show(getFragmentManager(), DIALOG_TAG_CONFIRM_BLOCK);
- }
- break;
- case R.id.menu_delete_distribution_list:
- GenericAlertDialog.newInstance(R.string.really_delete_distribution_list,
- R.string.really_delete_distribution_list_message,
- R.string.ok,
- R.string.cancel)
- .setTargetFragment(this)
- .setData(distributionListModel)
- .show(getFragmentManager(), CONFIRM_TAG_DELETE_DISTRIBUTION_LIST);
- break;
- case R.id.menu_shortcut:
- createShortcut();
- break;
- case R.id.menu_empty_chat:
- GenericAlertDialog.newInstance(R.string.empty_chat_title,
- R.string.empty_chat_confirm,
- R.string.ok,
- R.string.cancel)
- .setTargetFragment(this)
- .show(getFragmentManager(), DIALOG_TAG_EMPTY_CHAT);
- break;
- case R.id.menu_ballot_window_show:
- if (openBallotNoticeView.isShown()) {
- preferenceService.setBallotOverviewHidden(true);
- openBallotNoticeView.hide(true);
- } else {
- preferenceService.setBallotOverviewHidden(false);
- openBallotNoticeView.show(true);
- }
- break;
- case R.id.menu_ballot_show_all:
- Intent intent = new Intent(getContext(), BallotOverviewActivity.class);
- IntentDataUtil.addMessageReceiverToIntent(intent, messageReceiver);
- startActivity(intent);
- break;
- case R.id.menu_group_request_show_all:
- Intent groupRequestsOverviewIntent = new Intent(getContext(), IncomingGroupRequestActivity.class);
- IntentDataUtil.addMessageReceiverToIntent(groupRequestsOverviewIntent, messageReceiver);
- startActivity(groupRequestsOverviewIntent);
- break;
- case R.id.menu_group_requests_show:
- if (openGroupRequestNoticeView.isShown()) {
- preferenceService.setGroupRequestsOverviewHidden(true);
- openGroupRequestNoticeView.hide(true);
- } else {
- preferenceService.setGroupRequestsOverviewHidden(false);
- openGroupRequestNoticeView.show(true);
- }
- break;
- }
- return false;
- }
- private void emptyChat() {
- new EmptyChatAsyncTask(messageReceiver, messageService, conversationService, getFragmentManager(), false, new Runnable() {
- @Override
- public void run() {
- if (isAdded()) {
- synchronized (messageValues) {
- messageValues.clear();
- composeMessageAdapter.notifyDataSetChanged();
- }
- // empty draft
- ThreemaApplication.putMessageDraft(messageReceiver.getUniqueIdString(), "");
- messageText.setText(null);
- currentPageReferenceId = 0;
- onRefresh();
- ListenerManager.conversationListeners.handle(new ListenerManager.HandleListener<ConversationListener>() {
- @Override
- public void handle(ConversationListener listener) {
- if (!isGroupChat) {
- conversationService.reset();
- }
- listener.onModifiedAll();
- }
- });
- if (getActivity() != null) {
- getActivity().finish();
- }
- }
- }
- }).execute();
- }
- private void createShortcut() {
- if (!this.isGroupChat &&
- !this.isDistributionListChat &&
- ContactUtil.canReceiveVoipMessages(contactModel, blackListIdentityService) &&
- ConfigUtils.isCallsEnabled(getContext(), preferenceService, licenseService)) {
- ArrayList<SelectorDialogItem> items = new ArrayList<>();
- items.add(new SelectorDialogItem(getString(R.string.prefs_header_chat), R.drawable.ic_outline_chat_bubble_outline));
- items.add(new SelectorDialogItem(getString(R.string.threema_call), R.drawable.ic_call_outline));
- SelectorDialog selectorDialog = SelectorDialog.newInstance(getString(R.string.shortcut_choice_title), items, getString(R.string.cancel));
- selectorDialog.setTargetFragment(this, 0);
- selectorDialog.show(getFragmentManager(), DIALOG_TAG_CHOOSE_SHORTCUT_TYPE);
- } else {
- ShortcutUtil.createPinnedShortcut(messageReceiver, TYPE_CHAT);
- }
- }
- private void sendMessage() {
- if (typingIndicatorTextWatcher != null) {
- typingIndicatorTextWatcher.killEvents();
- }
- this.sendTextMessage();
- }
- @Override
- public void onActivityResult(int requestCode, int resultCode,
- final Intent intent) {
- if (wallpaperService != null && wallpaperService.handleActivityResult(this, requestCode, resultCode, intent, this.messageReceiver)) {
- setBackgroundWallpaper();
- return;
- }
- if (requestCode == ACTIVITY_ID_VOICE_RECORDER) {
- if (this.messagePlayerService != null) {
- this.messagePlayerService.resumeAll(getActivity(), messageReceiver, SOURCE_AUDIORECORDER);
- }
- }
- if (requestCode == ThreemaActivity.ACTIVITY_ID_ATTACH_MEDIA && resultCode == Activity.RESULT_OK) {
- this.lastMediaFilter = IntentDataUtil.getLastMediaFilterFromIntent(intent);
- }
- }
- private final SearchView.OnQueryTextListener queryTextListener = new SearchView.OnQueryTextListener() {
- // listener for search bar on top
- @Override
- public boolean onQueryTextChange(String newText) {
- composeMessageAdapter.getFilter().filter(newText);
- return true;
- }
- @Override
- public boolean onQueryTextSubmit(String query) {
- composeMessageAdapter.nextMatchPosition();
- return true;
- }
- };
- @Override
- public void onClick(String tag, int which, Object data) {
- if (DIALOG_TAG_CHOOSE_SHORTCUT_TYPE.equals(tag)) {
- int shortcutType = which + 1;
- ShortcutUtil.createPinnedShortcut(messageReceiver, shortcutType);
- }
- }
- @Override
- public void onCancel(String tag) {}
- @Override
- public void onNo(String tag) {}
- public class ComposeMessageAction implements ActionMode.Callback {
- private final int position;
- private MenuItem ackItem, decItem, quoteItem, logItem, discardItem, forwardItem, saveItem, copyItem, qrItem, shareItem, showText;
- ComposeMessageAction(int position) {
- this.position = position;
- longClickItem = position;
- }
- private void updateActionMenu() {
- // workaround for support library bug, see https://code.google.com/p/android/issues/detail?id=81192
- MenuItemCompat.setShowAsAction(ackItem, MenuItemCompat.SHOW_AS_ACTION_ALWAYS);
- MenuItemCompat.setShowAsAction(decItem, MenuItemCompat.SHOW_AS_ACTION_ALWAYS);
- MenuItemCompat.setShowAsAction(quoteItem, MenuItemCompat.SHOW_AS_ACTION_ALWAYS);
- MenuItemCompat.setShowAsAction(logItem, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
- MenuItemCompat.setShowAsAction(discardItem, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
- MenuItemCompat.setShowAsAction(saveItem, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
- MenuItemCompat.setShowAsAction(copyItem, MenuItemCompat.SHOW_AS_ACTION_ALWAYS);
- MenuItemCompat.setShowAsAction(forwardItem, MenuItemCompat.SHOW_AS_ACTION_ALWAYS);
- MenuItemCompat.setShowAsAction(qrItem, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
- MenuItemCompat.setShowAsAction(shareItem, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
- decItem.setVisible(false);
- ackItem.setVisible(false);
- quoteItem.setVisible(false);
- qrItem.setVisible(false);
- copyItem.setVisible(false);
- logItem.setVisible(false);
- saveItem.setVisible(false);
- shareItem.setVisible(false);
- showText.setVisible(false);
- if (selectedMessages.size() > 1) {
- boolean isForwardable = selectedMessages.size() <= MAX_FORWARDABLE_ITEMS;
- boolean isMedia = true;
- boolean isTextOnly = true;
- boolean isShareable = true;
- for (AbstractMessageModel message: selectedMessages) {
- if (isForwardable
- && (
- // if the media is not downloaded
- !message.isAvailable()
- // or the message is status message (unread or status)
- || message.isStatusMessage()
- // or a ballot
- || message.getType() == MessageType.BALLOT
- // or a voip status
- || message.getType() == MessageType.VOIP_STATUS)) {
- isForwardable = false;
- }
- if (isMedia && !message.isAvailable() ||
- (message.getType() != MessageType.IMAGE &&
- message.getType() != MessageType.VOICEMESSAGE &&
- message.getType() != MessageType.VIDEO &&
- message.getType() != MessageType.FILE)) {
- isMedia = false;
- }
- if (isTextOnly && message.getType() != MessageType.TEXT) {
- isTextOnly = false;
- }
- if (isShareable) {
- if (message.getType() != MessageType.IMAGE && message.getType() != MessageType.VIDEO && message.getType() != MessageType.FILE) {
- isShareable = false;
- }
- }
- }
- forwardItem.setVisible(isForwardable);
- saveItem.setVisible(isMedia);
- copyItem.setVisible(isTextOnly);
- shareItem.setVisible(isShareable);
- } else if (selectedMessages.size() == 1) {
- AbstractMessageModel selectedMessage = selectedMessages.get(0);
- if (selectedMessage.isStatusMessage()) {
- forwardItem.setVisible(false);
- copyItem.setVisible(true);
- logItem.setVisible(true);
- } else {
- boolean isValidReceiver = messageReceiver.validateSendingPermission(null);
- quoteItem.setVisible(isValidReceiver && QuoteUtil.isQuoteable(selectedMessage));
- decItem.setVisible(MessageUtil.canSendUserDecline(selectedMessage) && isValidReceiver);
- ackItem.setVisible(MessageUtil.canSendUserAcknowledge(selectedMessage) && isValidReceiver);
- logItem.setVisible(true);
- switch (selectedMessage.getType()) {
- case IMAGE:
- saveItem.setVisible(true);
- forwardItem.setVisible(true);
- shareItem.setVisible(true);
- if (!TestUtil.empty(selectedMessage.getCaption())) {
- copyItem.setVisible(true);
- }
- break;
- case VIDEO:
- saveItem.setVisible(selectedMessage.isAvailable());
- forwardItem.setVisible(selectedMessage.isAvailable());
- shareItem.setVisible(selectedMessage.isAvailable());
- break;
- case VOICEMESSAGE:
- saveItem.setVisible(selectedMessage.isAvailable());
- forwardItem.setVisible(selectedMessage.isAvailable());
- break;
- case FILE:
- if (selectedMessage.getFileData().getRenderingType() == FileData.RENDERING_DEFAULT) {
- MenuItemCompat.setShowAsAction(saveItem, MenuItemCompat.SHOW_AS_ACTION_ALWAYS);
- MenuItemCompat.setShowAsAction(forwardItem, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
- }
- saveItem.setVisible(selectedMessage.isAvailable());
- shareItem.setVisible(selectedMessage.isAvailable());
- forwardItem.setVisible(selectedMessage.isAvailable());
- if (!TestUtil.empty(selectedMessage.getCaption())) {
- copyItem.setVisible(true);
- }
- break;
- case BALLOT:
- saveItem.setVisible(false);
- forwardItem.setVisible(false);
- break;
- case TEXT:
- saveItem.setVisible(false);
- forwardItem.setVisible(true);
- copyItem.setVisible(true);
- qrItem.setVisible(true);
- shareItem.setVisible(true);
- showText.setVisible(true);
- break;
- case VOIP_STATUS:
- saveItem.setVisible(false);
- forwardItem.setVisible(false);
- copyItem.setVisible(false);
- qrItem.setVisible(false);
- shareItem.setVisible(false);
- logItem.setVisible(false);
- break;
- case LOCATION:
- shareItem.setVisible(true);
- break;
- default:
- break;
- }
- }
- }
- if (AppRestrictionUtil.isShareMediaDisabled(getContext())) {
- shareItem.setVisible(false);
- saveItem.setVisible(false);
- }
- }
- @Override
- public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- if (this.position == AbsListView.INVALID_POSITION) {
- return false;
- }
- if (convListView.getCheckedItemCount() < 1) {
- return false;
- }
- MenuInflater inflater = mode.getMenuInflater();
- if (inflater != null) {
- inflater.inflate(R.menu.action_compose_message, menu);
- }
- ConfigUtils.addIconsToOverflowMenu(null, menu);
- decItem = menu.findItem(R.id.menu_message_dec);
- ackItem = menu.findItem(R.id.menu_message_ack);
- logItem = menu.findItem(R.id.menu_message_log);
- discardItem = menu.findItem(R.id.menu_message_discard);
- forwardItem = menu.findItem(R.id.menu_message_forward);
- saveItem = menu.findItem(R.id.menu_message_save);
- copyItem = menu.findItem(R.id.menu_message_copy);
- qrItem = menu.findItem(R.id.menu_message_qrcode);
- shareItem = menu.findItem(R.id.menu_share);
- quoteItem = menu.findItem(R.id.menu_message_quote);
- showText = menu.findItem(R.id.menu_show_text);
- updateActionMenu();
- return true;
- }
- @Override
- public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
- final int checked = convListView.getCheckedItemCount();
- mode.setTitle(Integer.toString(checked));
- updateActionMenu();
- return true;
- }
- @Override
- public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- if (selectedMessages == null || selectedMessages.size() < 1) {
- mode.finish();
- return true;
- }
- switch (item.getItemId()) {
- case R.id.menu_message_copy:
- copySelectedMessagesToClipboard();
- mode.finish();
- break;
- case R.id.menu_message_discard:
- deleteSelectedMessages();
- break;
- case R.id.menu_message_forward:
- startForwardMessage();
- mode.finish();
- break;
- case R.id.menu_message_ack:
- sendUserAck();
- mode.finish();
- break;
- case R.id.menu_message_dec:
- sendUserDec();
- mode.finish();
- break;
- case R.id.menu_message_save:
- if (ConfigUtils.requestWriteStoragePermissions(activity, ComposeMessageFragment.this, PERMISSION_REQUEST_SAVE_MESSAGE)) {
- fileService.saveMedia(activity, coordinatorLayout, new CopyOnWriteArrayList<>(selectedMessages), false);
- }
- mode.finish();
- break;
- case R.id.menu_message_log:
- showMessageLog();
- mode.finish();
- break;
- case R.id.menu_message_qrcode:
- showAsQrCode(((ThreemaToolbarActivity) activity).getToolbar());
- mode.finish();
- break;
- case R.id.menu_share:
- shareMessages();
- mode.finish();
- break;
- case R.id.menu_message_quote:
- startQuoteMode(null, null);
- mode.finish();
- break;
- case R.id.menu_show_text:
- showTextChatBubble();
- mode.finish();
- break;
- default:
- return false;
- }
- return true;
- }
- @Override
- public void onDestroyActionMode(ActionMode mode) {
- actionMode = null;
- longClickItem = AbsListView.INVALID_POSITION;
- // handle done button
- convListView.clearChoices();
- convListView.requestLayout();
- convListView.post(new Runnable() {
- @Override
- public void run() {
- convListView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
- }
- });
- }
- }
- private void showTextChatBubble() {
- AbstractMessageModel messageModel = selectedMessages.get(0);
- Intent intent = new Intent(getContext(), TextChatBubbleActivity.class);
- IntentDataUtil.append(messageModel, intent);
- activity.startActivity(intent);
- activity.overridePendingTransition(R.anim.fast_fade_in, R.anim.fast_fade_out);
- }
- private void showAsQrCode(View v) {
- AbstractMessageModel messageModel = selectedMessages.get(0);
- if (messageModel != null && messageModel.getType() == MessageType.TEXT) {
- new QRCodePopup(getContext(), getActivity().getWindow().getDecorView(), getActivity()).show(v, messageModel.getBody());
- }
- }
- private void sendUserAck() {
- messageService.sendUserAcknowledgement(selectedMessages.get(0));
- Toast.makeText(getActivity(), R.string.message_acknowledged, Toast.LENGTH_SHORT).show();
- }
- /**
- * Send a Decline Message
- */
- private void sendUserDec() {
- messageService.sendUserDecline(selectedMessages.get(0));
- Toast.makeText(getActivity(), R.string.message_declined, Toast.LENGTH_SHORT).show();
- }
- public boolean onBackPressed() {
- logger.debug("onBackPressed");
- // dismiss emoji keyboard if it's showing instead of leaving activity
- if (emojiPicker != null && emojiPicker.isShown()) {
- emojiPicker.hide();
- return true;
- } else {
- if (mentionPopup != null && mentionPopup.isShowing()) {
- dismissMentionPopup();
- return true;
- }
- if (searchActionMode != null) {
- searchActionMode.finish();
- return true;
- }
- if (actionMode != null) {
- actionMode.finish();
- return true;
- }
- else if (ConfigUtils.isTabletLayout()) {
- if (actionBar != null) {
- actionBar.setDisplayUseLogoEnabled(true);
- actionBar.setDisplayShowCustomEnabled(false);
- }
- }
- return false;
- }
- }
- private void preserveListInstanceValues() {
- // this instance variable will probably survive an orientation change
- // since setRetainInstance() is set in onCreate()
- // so we don't put it into a bundle in onSaveInstanceState
- listInstancePosition = AbsListView.INVALID_POSITION;
- if (convListView != null && composeMessageAdapter != null) {
- if (convListView.getLastVisiblePosition() != composeMessageAdapter.getCount() - 1) {
- listInstancePosition = convListView.getFirstVisiblePosition();
- View v = convListView.getChildAt(0);
- listInstanceTop = (v == null) ? 0 : (v.getTop() - convListView.getPaddingTop());
- if (messageReceiver != null) {
- listInstanceReceiverId = messageReceiver.getUniqueIdString();
- }
- }
- }
- }
- @Override
- public void onSaveInstanceState(Bundle outState) {
- logger.debug("onSaveInstanceState");
- // some phones destroy the retained fragment upon going in background so we have to persist some data
- outState.putParcelable(CAMERA_URI, cameraUri);
- outState.putInt(ThreemaApplication.INTENT_DATA_GROUP, this.groupId);
- outState.putInt(ThreemaApplication.INTENT_DATA_DISTRIBUTION_LIST, this.distributionListId);
- outState.putString(ThreemaApplication.INTENT_DATA_CONTACT, this.identity);
- super.onSaveInstanceState(outState);
- }
- private Integer getCurrentPageReferenceId() {
- return this.currentPageReferenceId;
- }
- private void configureSearchWidget(final MenuItem menuItem) {
- SearchView searchView = (SearchView) menuItem.getActionView();
- if (searchView != null) {
- searchView.setOnQueryTextListener(queryTextListener);
- searchView.setQueryHint(getString(R.string.hint_search_keyword));
- searchView.setIconified(false);
- searchView.setOnCloseListener(() -> {
- if (searchActionMode != null) {
- searchActionMode.finish();
- }
- return false;
- });
- LinearLayout linearLayoutOfSearchView = (LinearLayout) searchView.getChildAt(0);
- if (linearLayoutOfSearchView != null) {
- linearLayoutOfSearchView.setGravity(Gravity.CENTER_VERTICAL);
- linearLayoutOfSearchView.setPadding(0, 0, 0, 0);
- searchCounter = (TextView) layoutInflater.inflate(R.layout.textview_search_action, null);
- linearLayoutOfSearchView.addView(searchCounter);
- FrameLayout searchPreviousLayout = (FrameLayout) layoutInflater.inflate(R.layout.button_search_action, null);
- searchPreviousButton = searchPreviousLayout.findViewById(R.id.search_button);
- searchPreviousButton.setImageDrawable(ConfigUtils.getThemedDrawable(activity, R.drawable.ic_keyboard_arrow_down_outline));
- searchPreviousButton.setScaleY(-1);
- searchPreviousButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- composeMessageAdapter.previousMatchPosition();
- }
- });
- linearLayoutOfSearchView.addView(searchPreviousLayout);
- FrameLayout searchNextLayout = (FrameLayout) layoutInflater.inflate(R.layout.button_search_action, null);
- searchNextButton = searchNextLayout.findViewById(R.id.search_button);
- searchProgress = searchNextLayout.findViewById(R.id.next_progress);
- searchNextButton.setImageDrawable(ConfigUtils.getThemedDrawable(activity, R.drawable.ic_keyboard_arrow_down_outline));
- searchNextButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- composeMessageAdapter.nextMatchPosition();
- }
- });
- linearLayoutOfSearchView.addView(searchNextLayout);
- }
- }
- }
- private class SearchActionMode implements ActionMode.Callback {
- @SuppressLint("StaticFieldLeak")
- @Override
- public boolean onCreateActionMode(ActionMode mode, final Menu menu) {
- composeMessageAdapter.clearFilter();
- activity.getMenuInflater().inflate(R.menu.action_compose_message_search, menu);
- final MenuItem item = menu.findItem(R.id.menu_action_search);
- final View actionView = item.getActionView();
- item.setActionView(R.layout.item_progress);
- item.expandActionView();
- if (bottomPanel != null) {
- bottomPanel.setVisibility(View.GONE);
- }
- if (emojiPicker != null && emojiPicker.isShown()) {
- emojiPicker.hide();
- }
- dismissMentionPopup();
- // load all records
- new AsyncTask<Void, Void, Void>() {
- List<AbstractMessageModel> messageModels;
- @Override
- protected Void doInBackground(Void... params) {
- messageModels = getAllRecords();
- return null;
- }
- @Override
- protected void onPostExecute(Void result) {
- if (messageModels != null && isAdded()) {
- item.collapseActionView();
- item.setActionView(actionView);
- configureSearchWidget(menu.findItem(R.id.menu_action_search));
- insertToList(messageModels, true, true, true);
- convListView.setSelection(Integer.MAX_VALUE);
- }
- }
- }.execute();
- return true;
- }
- @Override
- public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
- return false;
- }
- @Override
- public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- return false;
- }
- @Override
- public void onDestroyActionMode(ActionMode mode) {
- searchCounter = null;
- searchActionMode = null;
- if (composeMessageAdapter != null) {
- composeMessageAdapter.clearFilter();
- }
- if (bottomPanel != null) {
- bottomPanel.setVisibility(View.VISIBLE);
- }
- }
- }
- private void updateToolBarTitleInUIThread() {
- RuntimeUtil.runOnUiThread(this::updateToolbarTitle);
- }
- @UiThread
- private void updateContactModelData(final ContactModel contactModel) {
- //update header
- if(contactModel.getIdentity().equals(identity)) {
- updateToolbarTitle();
- }
- if (composeMessageAdapter != null) {
- composeMessageAdapter.resetCachedContactModelData(contactModel);
- }
- }
- final protected boolean requiredInstances() {
- if(!this.checkInstances()) {
- this.instantiate();
- }
- return this.checkInstances();
- }
- protected boolean checkInstances() {
- return TestUtil.required(
- this.preferenceService,
- this.userService,
- this.contactService,
- this.groupService,
- this.messageService,
- this.fileService,
- this.notificationService,
- this.distributionListService,
- this.messagePlayerService,
- this.blackListIdentityService,
- this.ballotService,
- this.conversationService,
- this.deviceService,
- this.wallpaperService,
- this.mutedChatsListService,
- this.ringtoneService,
- this.voipStateService
- );
- }
- protected void instantiate() {
- ServiceManager serviceManager = ThreemaApplication.getServiceManager();
- if (serviceManager != null) {
- this.preferenceService = serviceManager.getPreferenceService();
- try {
- this.userService = serviceManager.getUserService();
- this.contactService = serviceManager.getContactService();
- this.groupService = serviceManager.getGroupService();
- this.messageService = serviceManager.getMessageService();
- this.fileService = serviceManager.getFileService();
- this.notificationService = serviceManager.getNotificationService();
- this.distributionListService = serviceManager.getDistributionListService();
- this.messagePlayerService = serviceManager.getMessagePlayerService();
- this.blackListIdentityService = serviceManager.getBlackListService();
- this.ballotService = serviceManager.getBallotService();
- this.databaseServiceNew = serviceManager.getDatabaseServiceNew();
- this.conversationService = serviceManager.getConversationService();
- this.deviceService =serviceManager.getDeviceService();
- this.wallpaperService = serviceManager.getWallpaperService();
- this.mutedChatsListService = serviceManager.getMutedChatsListService();
- this.mentionOnlyChatsListService = serviceManager.getMentionOnlyChatsListService();
- this.hiddenChatsListService = serviceManager.getHiddenChatsListService();
- this.ringtoneService = serviceManager.getRingtoneService();
- this.voipStateService = serviceManager.getVoipStateService();
- this.downloadService = serviceManager.getDownloadService();
- this.licenseService = serviceManager.getLicenseService();
- } catch (Exception e) {
- LogUtil.exception(e, activity);
- }
- }
- }
- // Dialog callbacks
- @Override
- public void onYes(String tag, Object data) {
- switch (tag) {
- case CONFIRM_TAG_DELETE_DISTRIBUTION_LIST:
- final DistributionListModel dmodel = (DistributionListModel) data;
- if (dmodel != null) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- distributionListService.remove(dmodel);
- RuntimeUtil.runOnUiThread(() -> activity.finish());
- }
- }).start();
- }
- break;
- case ThreemaApplication.CONFIRM_TAG_CLOSE_BALLOT:
- BallotUtil.closeBallot((AppCompatActivity) getActivity(), (BallotModel) data, ballotService);
- break;
- case DIALOG_TAG_CONFIRM_CALL:
- VoipUtil.initiateCall((AppCompatActivity) getActivity(), contactModel, false, null);
- break;
- case DIALOG_TAG_EMPTY_CHAT:
- emptyChat();
- break;
- case DIALOG_TAG_CONFIRM_BLOCK:
- blackListIdentityService.toggle(activity, contactModel);
- updateBlockMenu();
- break;
- case DIALOG_TAG_CONFIRM_LINK:
- Uri uri = (Uri) data;
- LinkifyUtil.getInstance().openLink(getContext(), uri);
- break;
- default:
- break;
- }
- }
- @Override
- public void onNo(String tag, Object data) {
- //
- }
- @Override
- public void onEmojiPickerOpen() {
- }
- @Override
- public void onEmojiPickerClose() {
- }
- @Override
- public void onRequestPermissionsResult(int requestCode,
- @NonNull String[] permissions, @NonNull int[] grantResults) {
- if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- switch (requestCode) {
- case PERMISSION_REQUEST_SAVE_MESSAGE:
- fileService.saveMedia(activity, coordinatorLayout, new CopyOnWriteArrayList<>(selectedMessages), false);
- break;
- case PERMISSION_REQUEST_ATTACH_VOICE_MESSAGE:
- attachVoiceMessage();
- break;
- case PERMISSION_REQUEST_ATTACH_CAMERA:
- updateCameraButton();
- attachCamera();
- break;
- }
- } else {
- switch (requestCode) {
- case PERMISSION_REQUEST_SAVE_MESSAGE:
- if (!shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE)) {
- showPermissionRationale(R.string.permission_storage_required);
- }
- break;
- case PERMISSION_REQUEST_ATTACH_VOICE_MESSAGE:
- if (!shouldShowRequestPermissionRationale(Manifest.permission.RECORD_AUDIO)) {
- showPermissionRationale(R.string.permission_record_audio_required);
- }
- break;
- case PERMISSION_REQUEST_ATTACH_CAMERA:
- case PERMISSION_REQUEST_ATTACH_CAMERA_VIDEO:
- preferenceService.setCameraPermissionRequestShown(true);
- if (!shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
- showPermissionRationale(R.string.permission_camera_photo_required);
- }
- updateCameraButton();
- break;
- }
- }
- }
- /* properly dispose of popups */
- private void dismissMentionPopup() {
- if (this.mentionPopup != null) {
- try {
- this.mentionPopup.dismiss();
- } catch( final IllegalArgumentException e){
- // whatever
- } finally{
- this.mentionPopup = null;
- }
- }
- }
- private void dismissTooltipPopup(TooltipPopup tooltipPopup, boolean immediate) {
- try {
- if (tooltipPopup != null) {
- tooltipPopup.dismiss(immediate);
- }
- } catch (final IllegalArgumentException e) {
- // whatever
- }
- }
- public void markAsRead() {
- if (messageReceiver != null) {
- try {
- List<AbstractMessageModel> unreadMessages = messageReceiver.getUnreadMessages();
- if (unreadMessages != null && unreadMessages.size() > 0) {
- new Thread(new ReadMessagesRoutine(unreadMessages, this.messageService, this.notificationService)).start();
- }
- } catch (SQLException e) {
- logger.error("Exception", e);
- }
- }
- }
- @Override
- public void onConfigurationChanged(@NonNull Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- if (this.emojiPicker != null && this.emojiPicker.isShown()) {
- this.emojiPicker.hide();
- }
- EditTextUtil.hideSoftKeyboard(this.messageText);
- dismissMentionPopup();
- dismissTooltipPopup(workTooltipPopup, true);
- workTooltipPopup = null;
- if (ConfigUtils.isTabletLayout()) {
- // make sure layout changes after rotate are reflected in thumbnail size etc.
- saveMessageDraft();
- this.handleIntent(activity.getIntent());
- } else {
- if (isAdded()) {
- // refresh wallpaper to reflect orientation change
- this.wallpaperService.setupWallpaperBitmap(this.messageReceiver, this.wallpaperView, ConfigUtils.isLandscape(activity));
- }
- }
- }
- private void restoreMessageDraft() {
- if (this.messageReceiver != null && this.messageText != null && TestUtil.empty(this.messageText.getText())) {
- String messageDraft = ThreemaApplication.getMessageDraft(this.messageReceiver.getUniqueIdString());
- if (!TextUtils.isEmpty(messageDraft)) {
- this.messageText.setText("");
- this.messageText.append(messageDraft);
- } else {
- this.messageText.setText("");
- }
- }
- }
- private void saveMessageDraft() {
- if (this.messageReceiver != null) {
- String draft = ThreemaApplication.getMessageDraft(this.messageReceiver.getUniqueIdString());
- if (this.messageText.getText() != null) {
- ThreemaApplication.putMessageDraft(this.messageReceiver.getUniqueIdString(), this.messageText.getText().toString());
- }
- if (!TestUtil.empty(this.messageText.getText()) || !TestUtil.empty(draft)) {
- ListenerManager.conversationListeners.handle(new ListenerManager.HandleListener<ConversationListener>() {
- @Override
- public void handle(ConversationListener listener) {
- listener.onModifiedAll();
- }
- });
- }
- }
- }
- @Override
- public void onDismissed() {
- updateMenus();
- }
- @Override
- public void onKeyboardHidden() {
- if (getActivity() != null && isAdded()) {
- dismissMentionPopup();
- dismissTooltipPopup(workTooltipPopup, false);
- workTooltipPopup = null;
- }
- }
- @Override
- public void onKeyboardShown() {
- if (emojiPicker != null && emojiPicker.isShown()) {
- emojiPicker.hide();
- }
- }
- }
|