Convert ZimHostActivity to Kotlin

This commit is contained in:
Abdul Wadood 2019-11-24 20:00:22 +05:30
parent 13a63d58ac
commit 98f2c063b5
6 changed files with 314 additions and 330 deletions

View File

@ -10,9 +10,6 @@
</value>
</option>
<option name="LINE_SEPARATOR" value="&#10;" />
<AndroidXmlCodeStyleSettings>
<option name="ARRANGEMENT_SETTINGS_MIGRATED_TO_191" value="true" />
</AndroidXmlCodeStyleSettings>
<GroovyCodeStyleSettings>
<option name="ALIGN_MULTILINE_LIST_OR_MAP" value="false" />
<option name="ALIGN_NAMED_ARGS_IN_MAP" value="false" />

View File

@ -30,7 +30,7 @@ class AlertDialogShower @Inject constructor(
) : DialogShower {
override fun show(
dialog: KiwixDialog,
vararg clickListeners: () -> Unit
vararg clickListeners: (() -> Unit)?
) {
AlertDialog.Builder(activity, dialogStyle())

View File

@ -20,6 +20,6 @@ package org.kiwix.kiwixmobile.core.utils
interface DialogShower {
fun show(
dialog: KiwixDialog,
vararg clickListeners: () -> Unit
vararg clickListeners: (() -> Unit)?
)
}

View File

@ -1,322 +0,0 @@
/*
* Kiwix Android
* Copyright (c) 2019 Kiwix <android.kiwix.org>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.kiwix.kiwixmobile.core.webserver;
import android.app.ProgressDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.OnClick;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import javax.inject.Inject;
import kotlin.Unit;
import org.kiwix.kiwixmobile.core.R;
import org.kiwix.kiwixmobile.core.R2;
import org.kiwix.kiwixmobile.core.base.BaseActivity;
import org.kiwix.kiwixmobile.core.utils.AlertDialogShower;
import org.kiwix.kiwixmobile.core.utils.KiwixDialog;
import org.kiwix.kiwixmobile.core.utils.ServerUtils;
import org.kiwix.kiwixmobile.core.wifi_hotspot.HotspotService;
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.SelectionMode;
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BookOnDiskDelegate;
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskAdapter;
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem;
import static org.kiwix.kiwixmobile.core.wifi_hotspot.HotspotService.ACTION_CHECK_IP_ADDRESS;
import static org.kiwix.kiwixmobile.core.wifi_hotspot.HotspotService.ACTION_START_SERVER;
import static org.kiwix.kiwixmobile.core.wifi_hotspot.HotspotService.ACTION_STOP_SERVER;
public class ZimHostActivity extends BaseActivity implements
ZimHostCallbacks, ZimHostContract.View {
public static final String SELECTED_ZIM_PATHS_KEY = "selected_zim_paths";
private static final String TAG = "ZimHostActivity";
private static final String IP_STATE_KEY = "ip_state_key";
@BindView(R2.id.startServerButton)
Button startServerButton;
@BindView(R2.id.server_textView)
TextView serverTextView;
@BindView(R2.id.recycler_view_zim_host)
RecyclerView recyclerViewZimHost;
@Inject
ZimHostContract.Presenter presenter;
@Inject
AlertDialogShower alertDialogShower;
private BooksOnDiskAdapter booksAdapter;
private BookOnDiskDelegate.BookDelegate bookDelegate;
private HotspotService hotspotService;
private String ip;
private ServiceConnection serviceConnection;
private ProgressDialog progressDialog;
private HashSet<String> selectedBooksId = new HashSet<>();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_zim_host);
setUpToolbar();
bookDelegate =
new BookOnDiskDelegate.BookDelegate(sharedPreferenceUtil,
null,
null,
bookOnDiskItem -> {
select(bookOnDiskItem);
return Unit.INSTANCE;
});
bookDelegate.setSelectionMode(SelectionMode.MULTI);
booksAdapter = new BooksOnDiskAdapter(bookDelegate,
BookOnDiskDelegate.LanguageDelegate.INSTANCE
);
if (savedInstanceState != null) {
ip = savedInstanceState.getString(IP_STATE_KEY);
layoutServerStarted();
}
recyclerViewZimHost.setAdapter(booksAdapter);
presenter.attachView(this);
serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
hotspotService = ((HotspotService.HotspotBinder) service).getService();
hotspotService.registerCallBack(ZimHostActivity.this);
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
}
};
}
@OnClick(R2.id.startServerButton) void startStopServer() {
if (ServerUtils.isServerStarted) {
stopServer();
} else if (getSelectedBooksPath().size() > 0) {
startHotspotManuallyDialog();
} else {
Toast.makeText(this, R.string.no_books_selected_toast_message, Toast.LENGTH_SHORT).show();
}
}
private void stopServer() {
startService(createHotspotIntent(ACTION_STOP_SERVER));
}
private ArrayList<String> getSelectedBooksPath() {
ArrayList<String> selectedBooksPath = new ArrayList<>();
for (BooksOnDiskListItem item : booksAdapter.getItems()) {
if (item.isSelected()) {
BooksOnDiskListItem.BookOnDisk bookOnDisk = (BooksOnDiskListItem.BookOnDisk) item;
File file = bookOnDisk.getFile();
selectedBooksPath.add(file.getAbsolutePath());
Log.v(TAG, "ZIM PATH : " + file.getAbsolutePath());
}
}
return selectedBooksPath;
}
private void select(@NonNull BooksOnDiskListItem.BookOnDisk bookOnDisk) {
bookOnDisk.setSelected(!bookOnDisk.isSelected());
if (bookOnDisk.isSelected()) {
selectedBooksId.add(bookOnDisk.getBook().getId());
} else {
selectedBooksId.remove(bookOnDisk.getBook().getId());
}
booksAdapter.notifyDataSetChanged();
saveHostedBooks();
}
@Override protected void onStart() {
super.onStart();
bindService();
}
@Override protected void onStop() {
super.onStop();
unbindService();
}
private void bindService() {
bindService(new Intent(this, HotspotService.class), serviceConnection,
Context.BIND_AUTO_CREATE);
}
private void unbindService() {
if (hotspotService != null) {
unbindService(serviceConnection);
hotspotService.registerCallBack(null);
}
}
@Override protected void onResume() {
super.onResume();
presenter.loadBooks();
if (ServerUtils.isServerStarted) {
ip = ServerUtils.getSocketAddress();
layoutServerStarted();
}
}
private void saveHostedBooks() {
sharedPreferenceUtil.setHostedBooks(selectedBooksId);
}
private void layoutServerStarted() {
serverTextView.setText(getString(R.string.server_started_message, ip));
startServerButton.setText(getString(R.string.stop_server_label));
startServerButton.setBackgroundColor(getResources().getColor(R.color.stopServer));
bookDelegate.setSelectionMode(SelectionMode.NORMAL);
booksAdapter.notifyDataSetChanged();
}
private void layoutServerStopped() {
serverTextView.setText(getString(R.string.server_textview_default_message));
startServerButton.setText(getString(R.string.start_server_label));
startServerButton.setBackgroundColor(getResources().getColor(R.color.greenTick));
bookDelegate.setSelectionMode(SelectionMode.MULTI);
booksAdapter.notifyDataSetChanged();
}
@Override protected void onDestroy() {
super.onDestroy();
presenter.detachView();
}
private void setUpToolbar() {
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setTitle(getString(R.string.menu_host_books));
getSupportActionBar().setHomeButtonEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
toolbar.setNavigationOnClickListener(v -> onBackPressed());
}
//Advice user to turn on hotspot manually for API<26
private void startHotspotManuallyDialog() {
alertDialogShower.show(new KiwixDialog.StartHotspotManually(),
() -> {
launchTetheringSettingsScreen();
return Unit.INSTANCE;
},
null,
() -> {
progressDialog =
ProgressDialog.show(this,
getString(R.string.progress_dialog_starting_server), "",
true);
startService(createHotspotIntent(ACTION_CHECK_IP_ADDRESS));
return Unit.INSTANCE;
}
);
}
private Intent createHotspotIntent(String action) {
return new Intent(this, HotspotService.class).setAction(action);
}
@Override public void onServerStarted(@NonNull String ipAddress) {
this.ip = ipAddress;
layoutServerStarted();
}
@Override public void onServerStopped() {
layoutServerStopped();
}
@Override public void onServerFailedToStart() {
Toast.makeText(this, R.string.server_failed_toast_message, Toast.LENGTH_LONG).show();
}
private void launchTetheringSettingsScreen() {
final Intent intent = new Intent(Intent.ACTION_MAIN, null);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
final ComponentName cn =
new ComponentName("com.android.settings", "com.android.settings.TetherSettings");
intent.setComponent(cn);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
@Override protected void onSaveInstanceState(@Nullable Bundle outState) {
super.onSaveInstanceState(outState);
if (ServerUtils.isServerStarted) {
outState.putString(IP_STATE_KEY, ip);
}
}
@Override public void addBooks(@Nullable List<BooksOnDiskListItem> books) {
selectPreviouslyHostedBooks(books);
booksAdapter.setItems(books);
}
@Override public void onIpAddressValid() {
progressDialog.dismiss();
startService(createHotspotIntent(ACTION_START_SERVER).putStringArrayListExtra(
SELECTED_ZIM_PATHS_KEY, getSelectedBooksPath()));
}
@Override public void onIpAddressInvalid() {
progressDialog.dismiss();
Toast.makeText(this, R.string.server_failed_message,
Toast.LENGTH_SHORT)
.show();
}
private void selectPreviouslyHostedBooks(@Nullable List<BooksOnDiskListItem> books) {
selectedBooksId.addAll(sharedPreferenceUtil.getHostedBooks());
if (books != null && !books.isEmpty()) {
if (selectedBooksId.isEmpty()) {
// Select all books if no book ids are stored
for (BooksOnDiskListItem book : books) {
if (book instanceof BooksOnDiskListItem.BookOnDisk) {
selectedBooksId.add(((BooksOnDiskListItem.BookOnDisk) book).getBook().getId());
book.setSelected(true);
}
}
} else {
for (BooksOnDiskListItem book : books) {
if (book instanceof BooksOnDiskListItem.BookOnDisk) {
book.setSelected(
selectedBooksId.contains(((BooksOnDiskListItem.BookOnDisk) book).getBook().getId())
);
}
}
}
}
}
}

View File

@ -0,0 +1,309 @@
/*
* Kiwix Android
* Copyright (c) 2019 Kiwix <android.kiwix.org>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.kiwix.kiwixmobile.core.webserver
import android.app.ProgressDialog
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import android.util.Log
import android.widget.Toast
import androidx.appcompat.widget.Toolbar
import kotlinx.android.synthetic.main.activity_zim_host.recyclerViewZimHost
import kotlinx.android.synthetic.main.activity_zim_host.serverTextView
import kotlinx.android.synthetic.main.activity_zim_host.startServerButton
import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.base.BaseActivity
import org.kiwix.kiwixmobile.core.utils.AlertDialogShower
import org.kiwix.kiwixmobile.core.utils.KiwixDialog
import org.kiwix.kiwixmobile.core.utils.ServerUtils
import org.kiwix.kiwixmobile.core.wifi_hotspot.HotspotService
import org.kiwix.kiwixmobile.core.wifi_hotspot.HotspotService.ACTION_CHECK_IP_ADDRESS
import org.kiwix.kiwixmobile.core.wifi_hotspot.HotspotService.ACTION_START_SERVER
import org.kiwix.kiwixmobile.core.wifi_hotspot.HotspotService.ACTION_STOP_SERVER
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.SelectionMode
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BookOnDiskDelegate
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskAdapter
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem
import java.util.ArrayList
import java.util.HashSet
import javax.inject.Inject
class ZimHostActivity : BaseActivity(), ZimHostCallbacks, ZimHostContract.View {
@Inject
internal lateinit var presenter: ZimHostContract.Presenter
@Inject
internal lateinit var alertDialogShower: AlertDialogShower
private lateinit var booksAdapter: BooksOnDiskAdapter
private lateinit var bookDelegate: BookOnDiskDelegate.BookDelegate
private var hotspotService: HotspotService? = null
private var ip: String? = null
private var serviceConnection: ServiceConnection? = null
private var progressDialog: ProgressDialog? = null
private val selectedBooksId = HashSet<String>()
private val tag = "ZimHostActivity"
private val ipStateKey = "ip_state_key"
private val selectedBooksPath: ArrayList<String>
get() {
val selectedBooksPath = ArrayList<String>()
for (item in booksAdapter.items) {
if (item.isSelected) {
val (_, _, file) = item as BooksOnDiskListItem.BookOnDisk
selectedBooksPath.add(file.absolutePath)
Log.v(tag, "ZIM PATH : " + file.absolutePath)
}
}
return selectedBooksPath
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_zim_host)
setUpToolbar()
bookDelegate = BookOnDiskDelegate.BookDelegate(sharedPreferenceUtil, null, null,
{ bookOnDiskItem ->
select(bookOnDiskItem)
Unit
})
bookDelegate.selectionMode = SelectionMode.MULTI
booksAdapter = BooksOnDiskAdapter(
bookDelegate,
BookOnDiskDelegate.LanguageDelegate
)
if (savedInstanceState != null) {
ip = savedInstanceState.getString(ipStateKey)
layoutServerStarted()
}
recyclerViewZimHost.adapter = booksAdapter
presenter.attachView(this)
serviceConnection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
hotspotService = (service as HotspotService.HotspotBinder).service
hotspotService!!.registerCallBack(this@ZimHostActivity)
}
override fun onServiceDisconnected(arg0: ComponentName) {}
}
startServerButton.setOnClickListener { startStopServer() }
}
private fun startStopServer() {
when {
ServerUtils.isServerStarted -> stopServer()
selectedBooksPath.size > 0 -> startHotspotManuallyDialog()
else -> Toast.makeText(
this,
R.string.no_books_selected_toast_message,
Toast.LENGTH_SHORT
).show()
}
}
private fun stopServer() {
startService(createHotspotIntent(ACTION_STOP_SERVER))
}
private fun select(bookOnDisk: BooksOnDiskListItem.BookOnDisk) {
bookOnDisk.isSelected = !bookOnDisk.isSelected
if (bookOnDisk.isSelected) {
selectedBooksId.add(bookOnDisk.book.getId())
} else {
selectedBooksId.remove(bookOnDisk.book.getId())
}
booksAdapter.notifyDataSetChanged()
saveHostedBooks()
}
override fun onStart() {
super.onStart()
bindService()
}
override fun onStop() {
super.onStop()
unbindService()
}
private fun bindService() {
bindService(
Intent(this, HotspotService::class.java), serviceConnection,
Context.BIND_AUTO_CREATE
)
}
private fun unbindService() {
if (hotspotService != null) {
unbindService(serviceConnection)
hotspotService!!.registerCallBack(null)
}
}
override fun onResume() {
super.onResume()
presenter.loadBooks()
if (ServerUtils.isServerStarted) {
ip = ServerUtils.getSocketAddress()
layoutServerStarted()
}
}
private fun saveHostedBooks() {
sharedPreferenceUtil.hostedBooks = selectedBooksId
}
private fun layoutServerStarted() {
serverTextView.text = getString(R.string.server_started_message, ip)
startServerButton.text = getString(R.string.stop_server_label)
startServerButton.setBackgroundColor(resources.getColor(R.color.stopServer))
bookDelegate.selectionMode = SelectionMode.NORMAL
booksAdapter.notifyDataSetChanged()
}
private fun layoutServerStopped() {
serverTextView.text = getString(R.string.server_textview_default_message)
startServerButton.text = getString(R.string.start_server_label)
startServerButton.setBackgroundColor(resources.getColor(R.color.greenTick))
bookDelegate.selectionMode = SelectionMode.MULTI
booksAdapter.notifyDataSetChanged()
}
override fun onDestroy() {
super.onDestroy()
presenter.detachView()
}
private fun setUpToolbar() {
val toolbar = findViewById<Toolbar>(R.id.toolbar)
setSupportActionBar(toolbar)
supportActionBar!!.title = getString(R.string.menu_host_books)
supportActionBar!!.setHomeButtonEnabled(true)
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
toolbar.setNavigationOnClickListener { onBackPressed() }
}
// Advice user to turn on hotspot manually for API<26
private fun startHotspotManuallyDialog() {
alertDialogShower.show(KiwixDialog.StartHotspotManually(),
{
launchTetheringSettingsScreen()
Unit
},
null,
{
progressDialog = ProgressDialog.show(
this,
getString(R.string.progress_dialog_starting_server), "",
true
)
startService(createHotspotIntent(ACTION_CHECK_IP_ADDRESS))
Unit
}
)
}
private fun createHotspotIntent(action: String): Intent =
Intent(this, HotspotService::class.java).setAction(action)
override fun onServerStarted(ipAddress: String) {
ip = ipAddress
layoutServerStarted()
}
override fun onServerStopped() {
layoutServerStopped()
}
override fun onServerFailedToStart() {
Toast.makeText(this, R.string.server_failed_toast_message, Toast.LENGTH_LONG).show()
}
private fun launchTetheringSettingsScreen() {
val intent = Intent(Intent.ACTION_MAIN, null)
intent.addCategory(Intent.CATEGORY_LAUNCHER)
val cn = ComponentName("com.android.settings", "com.android.settings.TetherSettings")
intent.component = cn
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(intent)
}
override fun onSaveInstanceState(outState: Bundle?) {
super.onSaveInstanceState(outState)
if (ServerUtils.isServerStarted) {
outState!!.putString(ipStateKey, ip)
}
}
override fun addBooks(books: List<BooksOnDiskListItem>) {
selectPreviouslyHostedBooks(books)
booksAdapter.items = books
}
override fun onIpAddressValid() {
progressDialog!!.dismiss()
startService(
createHotspotIntent(ACTION_START_SERVER).putStringArrayListExtra(
SELECTED_ZIM_PATHS_KEY, selectedBooksPath
)
)
}
override fun onIpAddressInvalid() {
progressDialog!!.dismiss()
Toast.makeText(
this, R.string.server_failed_message,
Toast.LENGTH_SHORT
)
.show()
}
private fun selectPreviouslyHostedBooks(books: List<BooksOnDiskListItem>?) {
selectedBooksId.addAll(sharedPreferenceUtil.hostedBooks)
if (books != null && books.isNotEmpty()) {
if (selectedBooksId.isEmpty()) {
// Select all books if no book ids are stored
for (book in books) {
if (book is BooksOnDiskListItem.BookOnDisk) {
selectedBooksId.add(book.book.getId())
book.isSelected = true
}
}
} else {
books
.asSequence()
.filterIsInstance<BooksOnDiskListItem.BookOnDisk>()
.forEach { it.isSelected = selectedBooksId.contains(it.book.getId()) }
}
}
}
companion object {
const val SELECTED_ZIM_PATHS_KEY = "selected_zim_paths"
}
}

View File

@ -25,7 +25,7 @@
</com.google.android.material.appbar.AppBarLayout>
<TextView
android:id="@+id/server_textView"
android:id="@+id/serverTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
@ -37,7 +37,7 @@
/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view_zim_host"
android:id="@+id/recyclerViewZimHost"
android:layout_width="0dp"
android:layout_height="0dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
@ -46,7 +46,7 @@
app:layout_constraintBottom_toTopOf="@+id/startServerButton"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/server_textView"
app:layout_constraintTop_toBottomOf="@+id/serverTextView"
/>
<Button