Android WebViewにMTDeckを
適用しただけのアプリを作成した記録
2021 / 08 / 01
 
                    ここから少しの間、Android WebViewにmkizka様(Twitterアカウント)の作成されたMTDeck(GitHubのプロジェクトページ)を適用しただけのアプリを作成した記録を書いていきます。
元々完全に自分用に作成したもので、セキュリティや例外処理等はかなりテキトーですので、作成して使ってみる場合はそういったことを注意いただければと思います。なお、「普段モバイルでTweetDeckを使用したい」という場合は、さぶうぇいさん様(無理やり敬称)(Twitterアカウント)の作成されたMarinDeck(Google Play)をおススメします。
・こーゆー話はQiitaなどの方がいいのかもしれませんが、更新頻度がおそらく高くないままになり、また闇鍋化すると思われるためここで、ということになりました。しました。
・コード等は画像で載せていきますが、コピペ対策等ではなくコードに不備があった場合などに 危険なサイトになってしまわないようにするためです。
 
                     
                    
                        
WebViewの設定として、TweetDeck自体を動作させるために①~③の設定が必要になります。
                        ③は設定しなければJavaScriptが動作しないので警告ページに飛ばされます。
                        ①と②については、設定しないとロード画面のまま進みません。
                        ログイン画面にも行きません。
                    
・MTDeckを適用
まんまです。MTDeckを適用していきます。
                        下線④と⑤の部分にあたります。
 
                    自身がはまった個所を優先しますので、以下記述の順番が前後します。
                        ・リンク処理
                        以下はTweetDeck内のリンクの移動先を判定していますが、 この処理をしないとMTDeckの適用はできても、画像タップやツイート入力が正常動作しません。
                        コメントに書いてある通り、すべてtrueであればページ内の動作はしますが、ログインやログアウト、またログアウトキャンセル時に問題が発生するため、
                        移動先がmobile.twitter.comとtwetdeck.twitter.comの場合はfalseにして アプリ内で移動します。
                    
 
                    
                        
twitter.comについては、TweetDeckが短縮でないTwitter内URLをリンクとして持つ場合があるための処理です。
このアプリは
                        「公式クライアントで処理した方がよいと思われるものはそうする」 という発想で制作しており、アカウントなどへのリンクであれば
                        (インストールされていれば)公式クライアントで表示すればよいと思いますが、リリースノートへのアドレスが twitter.com/i/tweetdeck_release_notes
                        になっており、公式クライアントの動作としては「twitterへの移動であるから処理しようとするが、不可能なため処理が戻される」ことになり、少しひねらないとリリースノートを表示することができなくなります。
このため、パスを判定してリリースノートのページである場合に限り、サブとして用意したWebViewで表示し、twitter.comの別のページ及び他サイトへは、他のアプリで開くようになっています。するようにしています。TweetDeck内への移動の場合、jswvc()を呼んでいます。
                    
 
                    サブのWebViewはほぼ素の状態ですが、リリースノート以外も表示するケースを考え、バックキーで履歴を戻るようにしています。
・MTDeck適用
 
                    まず、TweetDeckが読み込まれてからMTDeckを適用しないと意味がないため、WebViewClientを継承しonPageFinishedを使用して、
                        ページロード後にMTDeckがあたるようにしています。
                        その内部で、AS.getAString()というクラスにMTDeck本体(assets/js/mtdeck.user.js)のパスを渡しています。
                        余談ですが、ASはasset stringの略なのですが、こういう適当な名前を付けていくと、
                        似たような名前を付けたい処理が出てきたりして割と後で苦労します。しました。
                        AS.getAString()については後述します。
次いで、AS.getAString()から戻ったScriptの文字列をevaluateJavaScriptで処理しています。 今回Callbackは不要なので、nullとしています。
そこから、OnCreateで一度呼んだMTDMuwc()をもう一度呼んでいます。 TweetDeckのログインキャンセル時にTweetDeckが再読み込みされるのですが、 この処理を再度適用しないと上述の正常動作しない現象が発生します。 入れ子になるので危なっかしいな、と思ったのですが動作自体は大丈夫でした(適当)。 原因はなんとなく想像がつきますが、想像なので述べないことにします。 当初上述の正常動作しない状況で、内部のリンク処理が上手くいっていないのだろうと (カンで)推測しており、 この処理をjswvc()とひとくくりにしようとしていましたが、 同時適用ではうまくいかず、 順次適用でないと動作しませんでした。
・スクリプト文字列処理
 
                    AS側は、Main側から投げられたパスのファイルをInputStreamとして読み込み、 各行に改行を加えた文字列を構築して返しています。 文字列ががnullでないか判定していますが、 「nullでない場合ではない」 のであれば当然それはnullです(当たり前)。 私は横着なので 「Stringだしとりあえず空文字渡しておくか」 という処理をしています。 また余談ですが、複数のScriptをあてる場合、適用したページが読み込まれたあとに再適用する必要があるため、 こういった処理を繰り返すことになると思います。
                        一連の処理の流れとしては、
                        ローカルの(現状TweetDeckへリダイレクトするだけの)start.html読み込み
                        ↓
                        Url別処理
                        ↓
                        MTDeck適用
                        ↓
                        再度Url別処理
                        となっていますが、 上記の内容から、「OnCreate内でTweetDeckをloadUrl()→jswvc()→MTDMuwvc()でいいのでは?」
                        と思われる方もおられると思います。 まあ実際そうだと思います(また適当)。この流れは、
                        ・shouldOverrideUrlLoadingのリダイレクト時の挙動確認
                        ・もうちょっと大掛かりなことを考えていた時の、start.html拡張予定の名残
                        です。
                    
・ファイルアップロード
                        TweetDeck/MTDeckの機能上、画像や動画のアップロードがしたい。
                        ということで、ファイルアップロード用の処理です。
                        ⑥の部分です。
                    
 
                    
                        
これはもう本当に↓のページに助けていただいたので、リンクを(無断で)貼ります。
                        もう出ないなんて言わせないWebViewでファイルアップロードするダイアログの表示
                        見た目はいじっていますが、処理はほぼこのままです。
                    
 
                    上述の通りほぼまるごとパク…持ってきているので、特にハマりなどはなかったのですが、 アップロードできるファイルは限定しないといけないので、 setTypeとputExtraで4種類に絞っています。
・Chromiumによって表示される「動画のダウンロードメニュー」対応
                        TweetDeckの本来の機能ではないようだし、
                        「いいのか?これ…」
                        という面もあるのですが、ChromiumでTweetDeck内の動画を再生するとき、
                        「ダウンロード」
                        メニューが表示されます。されてしまいます。
                        ありようとして迷いはあったのですが、
                        「表示されても動作しないのはなあ…」
                        というしょーもない動機で対応しました。
                        といっても、DownloadListenerでひっかけてDLクラスに投げてるだけですが。
                    
 
                     
                    
                        
DLクラスも、基本的にはDownloadManagerに丸投げです。
                        Mainから投げられたUrlの文字列を受け取り、DLManager
                        getLastPathSegment()で取得した名前をファイル名として放り込んでいます。
                    
 
                    上述の通りほぼまるごとパク…持ってきているので、特にハマりなどはなかったのですが、 アップロードできるファイルは限定しないといけないので、 setTypeとputExtraで4種類に絞っています。
 
                    
コメントにも書いていますが、nullチェックなどはMain側の判定を信じて
                        (また配布するものでないこともあり)やっていません(怠惰)。
                        なお、どうにもアレな処理が途中に見えますが、次の回で書きます。
                        CookieManagerは現状不要な気もしますが、今後変わりうる状況やあっても腐らないので入れてあります。
                        冷蔵庫か。
これもほぼ丸投げなので特にハマりもありませんでした。
                        が。
                        ダウンロードされた動画は、Android10以降では(おそらくScoped Strageとの仕様により)
                        端末内ではすぐに(MediaStoreの解決ができれば)再生可能ですが、外部等から接続してもすぐには適切に表示されません。
                        見た目上ファイルが存在していても、実ファイルではなくそれへのリンクになっていると思われ、
                        それを例えばPCに移動しても再生できません。
                        すぐに、というのは手持ちのAndroid10端末では、しばらく時間がたった後で当該ファイルを外部へ移動したところ、
                        再生可能な状態になったためで、無責任なので原因は確認していません。
                    
・コンテキストメニューと画像ダウンロード
                        ⑧の処理です。
                    
 
                    
                        自分用機能です。
                        ロングタップで画像をダウンロードできます。
                        動画についてああいった書き方をしておいてこれです。すみません。
                    

コメントにありますが、外部アプリで開くこともできます。 通常Chromeが開くと思います。
あとは動画と同様DLクラスでDownloadManagerに丸投げです。

画像は前回のものの再掲になります。 前回触れたアレな処理ですが、画像の種類の判定をクエリで行っています。 ダサいですね。すみません。精進します。
これで今回のアプリの作成記録は終わりです。 機能自体はおおよそ必要十分だと思っていますが、先に書いたように問題発生時の処理が 適当なため、動作に不具合アが出るかもしれません。 「これひどくね」「もうちょっとこう…」 等あれば何とかしてください。 教えていただけると喜びます。怠け者なのでアレですが。
以上です。お付き合いいただいてありがとうございました。
                        ・お詫び
                        この記事、怠け者であるがゆえに半年寝かせてしまいました。
                        そのため内容がやや古く、現状にそぐわないことがあるかもしれません。