Added Select all and source migration to selected manga on library screen
@ -126,6 +126,16 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
|||||||
|
|
||||||
subscriptions += controller.selectionRelay
|
subscriptions += controller.selectionRelay
|
||||||
.subscribe { onSelectionChanged(it) }
|
.subscribe { onSelectionChanged(it) }
|
||||||
|
|
||||||
|
subscriptions += controller.selectAllRelay
|
||||||
|
.subscribe {
|
||||||
|
if (it == category.id) {
|
||||||
|
adapter.currentItems.forEach { item ->
|
||||||
|
controller.setSelection(item.manga, true)
|
||||||
|
}
|
||||||
|
controller.invalidateActionMode()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onRecycle() {
|
fun onRecycle() {
|
||||||
|
@ -5,20 +5,24 @@ import android.content.Intent
|
|||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import com.google.android.material.tabs.TabLayout
|
import android.view.LayoutInflater
|
||||||
import androidx.core.graphics.drawable.DrawableCompat
|
import android.view.Menu
|
||||||
|
import android.view.MenuInflater
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.view.WindowInsets
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.view.ActionMode
|
import androidx.appcompat.view.ActionMode
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import android.view.*
|
import androidx.core.graphics.drawable.DrawableCompat
|
||||||
import androidx.appcompat.widget.ActionBarContextView
|
|
||||||
import androidx.core.view.GravityCompat
|
import androidx.core.view.GravityCompat
|
||||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||||
import com.bluelinelabs.conductor.ControllerChangeType
|
import com.bluelinelabs.conductor.ControllerChangeType
|
||||||
import com.f2prateek.rx.preferences.Preference
|
import com.f2prateek.rx.preferences.Preference
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import com.google.android.material.tabs.TabLayout
|
||||||
import com.jakewharton.rxbinding.support.v4.view.pageSelections
|
import com.jakewharton.rxbinding.support.v4.view.pageSelections
|
||||||
import com.jakewharton.rxbinding.support.v7.widget.queryTextChanges
|
import com.jakewharton.rxbinding.support.v7.widget.queryTextChanges
|
||||||
import com.jakewharton.rxrelay.BehaviorRelay
|
import com.jakewharton.rxrelay.BehaviorRelay
|
||||||
@ -37,7 +41,15 @@ import eu.kanade.tachiyomi.ui.category.CategoryController
|
|||||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||||
import eu.kanade.tachiyomi.ui.manga.MangaController
|
import eu.kanade.tachiyomi.ui.manga.MangaController
|
||||||
import eu.kanade.tachiyomi.ui.migration.MigrationController
|
import eu.kanade.tachiyomi.ui.migration.MigrationController
|
||||||
import eu.kanade.tachiyomi.util.*
|
import eu.kanade.tachiyomi.ui.migration.MigrationInterface
|
||||||
|
import eu.kanade.tachiyomi.ui.migration.SearchController
|
||||||
|
import eu.kanade.tachiyomi.util.doOnApplyWindowInsets
|
||||||
|
import eu.kanade.tachiyomi.util.inflate
|
||||||
|
import eu.kanade.tachiyomi.util.marginBottom
|
||||||
|
import eu.kanade.tachiyomi.util.marginTop
|
||||||
|
import eu.kanade.tachiyomi.util.snack
|
||||||
|
import eu.kanade.tachiyomi.util.toast
|
||||||
|
import eu.kanade.tachiyomi.util.updatePaddingRelative
|
||||||
import kotlinx.android.synthetic.main.library_controller.*
|
import kotlinx.android.synthetic.main.library_controller.*
|
||||||
import kotlinx.android.synthetic.main.main_activity.*
|
import kotlinx.android.synthetic.main.main_activity.*
|
||||||
import rx.Subscription
|
import rx.Subscription
|
||||||
@ -46,7 +58,6 @@ import uy.kohesive.injekt.Injekt
|
|||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
|
|
||||||
class LibraryController(
|
class LibraryController(
|
||||||
bundle: Bundle? = null,
|
bundle: Bundle? = null,
|
||||||
private val preferences: PreferencesHelper = Injekt.get()
|
private val preferences: PreferencesHelper = Injekt.get()
|
||||||
@ -55,7 +66,8 @@ class LibraryController(
|
|||||||
SecondaryDrawerController,
|
SecondaryDrawerController,
|
||||||
ActionMode.Callback,
|
ActionMode.Callback,
|
||||||
ChangeMangaCategoriesDialog.Listener,
|
ChangeMangaCategoriesDialog.Listener,
|
||||||
DeleteLibraryMangasDialog.Listener {
|
DeleteLibraryMangasDialog.Listener,
|
||||||
|
MigrationInterface {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Position of the active category.
|
* Position of the active category.
|
||||||
@ -78,6 +90,11 @@ class LibraryController(
|
|||||||
*/
|
*/
|
||||||
val selectedMangas = mutableSetOf<Manga>()
|
val selectedMangas = mutableSetOf<Manga>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current mangas to move.
|
||||||
|
*/
|
||||||
|
private var migratingMangas = mutableSetOf<Manga>()
|
||||||
|
|
||||||
private var selectedCoverManga: Manga? = null
|
private var selectedCoverManga: Manga? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -95,6 +112,11 @@ class LibraryController(
|
|||||||
*/
|
*/
|
||||||
val libraryMangaRelay: BehaviorRelay<LibraryMangaEvent> = BehaviorRelay.create()
|
val libraryMangaRelay: BehaviorRelay<LibraryMangaEvent> = BehaviorRelay.create()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relay to notify the library's viewpager to select all manga
|
||||||
|
*/
|
||||||
|
val selectAllRelay: PublishRelay<Int> = PublishRelay.create()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of manga per row in grid mode.
|
* Number of manga per row in grid mode.
|
||||||
*/
|
*/
|
||||||
@ -425,11 +447,36 @@ class LibraryController(
|
|||||||
}
|
}
|
||||||
R.id.action_move_to_category -> showChangeMangaCategoriesDialog()
|
R.id.action_move_to_category -> showChangeMangaCategoriesDialog()
|
||||||
R.id.action_delete -> deleteMangasFromLibrary()
|
R.id.action_delete -> deleteMangasFromLibrary()
|
||||||
|
R.id.action_select_all -> {
|
||||||
|
adapter?.categories?.getOrNull(library_pager.currentItem)?.id?.let {
|
||||||
|
selectAllRelay.call(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
R.id.action_migrate -> startMangaMigration()
|
||||||
else -> return false
|
else -> return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun migrateManga(prevManga: Manga, manga: Manga, replace: Boolean): Manga? {
|
||||||
|
presenter.migrateManga(prevManga, manga, replace = replace)
|
||||||
|
val nextManga = migratingMangas.firstOrNull() ?: return null
|
||||||
|
migratingMangas.remove(nextManga)
|
||||||
|
return nextManga
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startMangaMigration() {
|
||||||
|
migratingMangas.clear()
|
||||||
|
migratingMangas.addAll(selectedMangas)
|
||||||
|
destroyActionModeIfNeeded()
|
||||||
|
val manga = migratingMangas.firstOrNull() ?: return
|
||||||
|
val searchController = SearchController(manga)
|
||||||
|
searchController.totalProgress = migratingMangas.size
|
||||||
|
searchController.targetController = this
|
||||||
|
router.pushController(searchController.withFadeTransaction())
|
||||||
|
migratingMangas.remove(manga)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroyActionMode(mode: ActionMode?) {
|
override fun onDestroyActionMode(mode: ActionMode?) {
|
||||||
// Clear all the manga selections and notify child views.
|
// Clear all the manga selections and notify child views.
|
||||||
selectedMangas.clear()
|
selectedMangas.clear()
|
||||||
|
@ -11,12 +11,16 @@ import eu.kanade.tachiyomi.data.download.DownloadManager
|
|||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||||
import eu.kanade.tachiyomi.source.LocalSource
|
import eu.kanade.tachiyomi.source.LocalSource
|
||||||
|
import eu.kanade.tachiyomi.source.Source
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||||
|
import eu.kanade.tachiyomi.ui.migration.MigrationFlags
|
||||||
import eu.kanade.tachiyomi.util.combineLatest
|
import eu.kanade.tachiyomi.util.combineLatest
|
||||||
import eu.kanade.tachiyomi.util.isNullOrUnsubscribed
|
import eu.kanade.tachiyomi.util.isNullOrUnsubscribed
|
||||||
|
import eu.kanade.tachiyomi.util.syncChaptersWithSource
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.Subscription
|
import rx.Subscription
|
||||||
import rx.android.schedulers.AndroidSchedulers
|
import rx.android.schedulers.AndroidSchedulers
|
||||||
@ -355,6 +359,79 @@ class LibraryPresenter(
|
|||||||
db.setMangaCategories(mc, mangas)
|
db.setMangaCategories(mc, mangas)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun migrateManga(prevManga: Manga, manga: Manga, replace: Boolean) {
|
||||||
|
val source = sourceManager.get(manga.source) ?: return
|
||||||
|
|
||||||
|
//state = state.copy(isReplacingManga = true)
|
||||||
|
|
||||||
|
Observable.defer { source.fetchChapterList(manga) }
|
||||||
|
.onErrorReturn { emptyList() }
|
||||||
|
.doOnNext { migrateMangaInternal(source, it, prevManga, manga, replace) }
|
||||||
|
.onErrorReturn { emptyList() }
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
//.doOnUnsubscribe { state = state.copy(isReplacingManga = false) }
|
||||||
|
.subscribe()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun migrateMangaInternal(source: Source, sourceChapters: List<SChapter>,
|
||||||
|
prevManga: Manga, manga: Manga, replace: Boolean) {
|
||||||
|
|
||||||
|
val flags = preferences.migrateFlags().getOrDefault()
|
||||||
|
val migrateChapters = MigrationFlags.hasChapters(flags)
|
||||||
|
val migrateCategories = MigrationFlags.hasCategories(flags)
|
||||||
|
val migrateTracks = MigrationFlags.hasTracks(flags)
|
||||||
|
|
||||||
|
db.inTransaction {
|
||||||
|
// Update chapters read
|
||||||
|
if (migrateChapters) {
|
||||||
|
try {
|
||||||
|
syncChaptersWithSource(db, sourceChapters, manga, source)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// Worst case, chapters won't be synced
|
||||||
|
}
|
||||||
|
|
||||||
|
val prevMangaChapters = db.getChapters(prevManga).executeAsBlocking()
|
||||||
|
val maxChapterRead =
|
||||||
|
prevMangaChapters.filter { it.read }.maxBy { it.chapter_number }?.chapter_number
|
||||||
|
if (maxChapterRead != null) {
|
||||||
|
val dbChapters = db.getChapters(manga).executeAsBlocking()
|
||||||
|
for (chapter in dbChapters) {
|
||||||
|
if (chapter.isRecognizedNumber && chapter.chapter_number <= maxChapterRead) {
|
||||||
|
chapter.read = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
db.insertChapters(dbChapters).executeAsBlocking()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Update categories
|
||||||
|
if (migrateCategories) {
|
||||||
|
val categories = db.getCategoriesForManga(prevManga).executeAsBlocking()
|
||||||
|
val mangaCategories = categories.map { MangaCategory.create(manga, it) }
|
||||||
|
db.setMangaCategories(mangaCategories, listOf(manga))
|
||||||
|
}
|
||||||
|
// Update track
|
||||||
|
if (migrateTracks) {
|
||||||
|
val tracks = db.getTracks(prevManga).executeAsBlocking()
|
||||||
|
for (track in tracks) {
|
||||||
|
track.id = null
|
||||||
|
track.manga_id = manga.id!!
|
||||||
|
}
|
||||||
|
db.insertTracks(tracks).executeAsBlocking()
|
||||||
|
}
|
||||||
|
// Update favorite status
|
||||||
|
if (replace) {
|
||||||
|
prevManga.favorite = false
|
||||||
|
db.updateMangaFavorite(prevManga).executeAsBlocking()
|
||||||
|
}
|
||||||
|
manga.favorite = true
|
||||||
|
db.updateMangaFavorite(manga).executeAsBlocking()
|
||||||
|
|
||||||
|
// SearchPresenter#networkToLocalManga may have updated the manga title, so ensure db gets updated title
|
||||||
|
db.updateMangaTitle(manga).executeAsBlocking()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update cover with local file.
|
* Update cover with local file.
|
||||||
*
|
*
|
||||||
|
@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.ui.migration
|
|||||||
|
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
@ -20,7 +19,8 @@ import kotlinx.android.synthetic.main.migration_controller.*
|
|||||||
|
|
||||||
class MigrationController : NucleusController<MigrationPresenter>(),
|
class MigrationController : NucleusController<MigrationPresenter>(),
|
||||||
FlexibleAdapter.OnItemClickListener,
|
FlexibleAdapter.OnItemClickListener,
|
||||||
SourceAdapter.OnSelectClickListener {
|
SourceAdapter.OnSelectClickListener,
|
||||||
|
MigrationInterface {
|
||||||
|
|
||||||
private var adapter: FlexibleAdapter<IFlexible<*>>? = null
|
private var adapter: FlexibleAdapter<IFlexible<*>>? = null
|
||||||
|
|
||||||
@ -38,6 +38,13 @@ class MigrationController : NucleusController<MigrationPresenter>(),
|
|||||||
return inflater.inflate(R.layout.migration_controller, container, false)
|
return inflater.inflate(R.layout.migration_controller, container, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun searchController(manga:Manga): SearchController {
|
||||||
|
val controller = SearchController(manga)
|
||||||
|
controller.targetController = this
|
||||||
|
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View) {
|
override fun onViewCreated(view: View) {
|
||||||
super.onViewCreated(view)
|
super.onViewCreated(view)
|
||||||
|
|
||||||
@ -112,12 +119,9 @@ class MigrationController : NucleusController<MigrationPresenter>(),
|
|||||||
onItemClick(view, position)
|
onItemClick(view, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun migrateManga(prevManga: Manga, manga: Manga) {
|
override fun migrateManga(prevManga: Manga, manga: Manga, replace: Boolean): Manga? {
|
||||||
presenter.migrateManga(prevManga, manga, replace = true)
|
presenter.migrateManga(prevManga, manga, replace)
|
||||||
}
|
return null
|
||||||
|
|
||||||
fun copyManga(prevManga: Manga, manga: Manga) {
|
|
||||||
presenter.migrateManga(prevManga, manga, replace = false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class LoadingController : DialogController() {
|
class LoadingController : DialogController() {
|
||||||
@ -136,3 +140,7 @@ class MigrationController : NucleusController<MigrationPresenter>(),
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface MigrationInterface {
|
||||||
|
fun migrateManga(prevManga: Manga, manga: Manga, replace: Boolean): Manga?
|
||||||
|
}
|
||||||
|
@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.data.database.models.Manga
|
|||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||||
|
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||||
import eu.kanade.tachiyomi.ui.catalogue.global_search.CatalogueSearchController
|
import eu.kanade.tachiyomi.ui.catalogue.global_search.CatalogueSearchController
|
||||||
import eu.kanade.tachiyomi.ui.catalogue.global_search.CatalogueSearchPresenter
|
import eu.kanade.tachiyomi.ui.catalogue.global_search.CatalogueSearchPresenter
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
@ -17,6 +18,16 @@ class SearchController(
|
|||||||
) : CatalogueSearchController(manga?.title) {
|
) : CatalogueSearchController(manga?.title) {
|
||||||
|
|
||||||
private var newManga: Manga? = null
|
private var newManga: Manga? = null
|
||||||
|
private var progress = 1
|
||||||
|
var totalProgress = 0
|
||||||
|
|
||||||
|
override fun getTitle(): String? {
|
||||||
|
if (totalProgress > 1) {
|
||||||
|
return "($progress/$totalProgress) ${super.getTitle()}"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return super.getTitle()
|
||||||
|
}
|
||||||
|
|
||||||
override fun createPresenter(): CatalogueSearchPresenter {
|
override fun createPresenter(): CatalogueSearchPresenter {
|
||||||
return SearchPresenter(initialQuery, manga!!)
|
return SearchPresenter(initialQuery, manga!!)
|
||||||
@ -35,21 +46,32 @@ class SearchController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun migrateManga() {
|
fun migrateManga() {
|
||||||
val target = targetController as? MigrationController ?: return
|
val target = targetController as? MigrationInterface ?: return
|
||||||
val manga = manga ?: return
|
val manga = manga ?: return
|
||||||
val newManga = newManga ?: return
|
val newManga = newManga ?: return
|
||||||
|
|
||||||
router.popController(this)
|
val nextManga = target.migrateManga(manga, newManga, true)
|
||||||
target.migrateManga(manga, newManga)
|
replaceWithNewSearchController(nextManga)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun copyManga() {
|
fun copyManga() {
|
||||||
val target = targetController as? MigrationController ?: return
|
val target = targetController as? MigrationInterface ?: return
|
||||||
val manga = manga ?: return
|
val manga = manga ?: return
|
||||||
val newManga = newManga ?: return
|
val newManga = newManga ?: return
|
||||||
|
|
||||||
router.popController(this)
|
val nextManga = target.migrateManga(manga, newManga, false)
|
||||||
target.copyManga(manga, newManga)
|
replaceWithNewSearchController(nextManga)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun replaceWithNewSearchController(manga: Manga?) {
|
||||||
|
if (manga != null) {
|
||||||
|
router.popCurrentController()
|
||||||
|
val searchController = SearchController(manga)
|
||||||
|
searchController.targetController = targetController
|
||||||
|
searchController.progress = progress + 1
|
||||||
|
searchController.totalProgress = totalProgress
|
||||||
|
router.replaceTopController(searchController.withFadeTransaction())
|
||||||
|
} else router.popController(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMangaClick(manga: Manga) {
|
override fun onMangaClick(manga: Manga) {
|
||||||
|
BIN
app/src/main/res/drawable-hdpi/baseline_swap_calls_white_18.png
Normal file
After Width: | Height: | Size: 248 B |
BIN
app/src/main/res/drawable-hdpi/baseline_swap_calls_white_24.png
Normal file
After Width: | Height: | Size: 291 B |
BIN
app/src/main/res/drawable-hdpi/baseline_swap_calls_white_36.png
Normal file
After Width: | Height: | Size: 426 B |
BIN
app/src/main/res/drawable-hdpi/baseline_swap_calls_white_48.png
Normal file
After Width: | Height: | Size: 443 B |
BIN
app/src/main/res/drawable-mdpi/baseline_swap_calls_white_18.png
Normal file
After Width: | Height: | Size: 204 B |
BIN
app/src/main/res/drawable-mdpi/baseline_swap_calls_white_24.png
Normal file
After Width: | Height: | Size: 186 B |
BIN
app/src/main/res/drawable-mdpi/baseline_swap_calls_white_36.png
Normal file
After Width: | Height: | Size: 291 B |
BIN
app/src/main/res/drawable-mdpi/baseline_swap_calls_white_48.png
Normal file
After Width: | Height: | Size: 323 B |
BIN
app/src/main/res/drawable-xhdpi/baseline_swap_calls_white_18.png
Normal file
After Width: | Height: | Size: 291 B |
BIN
app/src/main/res/drawable-xhdpi/baseline_swap_calls_white_24.png
Normal file
After Width: | Height: | Size: 323 B |
BIN
app/src/main/res/drawable-xhdpi/baseline_swap_calls_white_36.png
Normal file
After Width: | Height: | Size: 443 B |
BIN
app/src/main/res/drawable-xhdpi/baseline_swap_calls_white_48.png
Normal file
After Width: | Height: | Size: 580 B |
After Width: | Height: | Size: 426 B |
After Width: | Height: | Size: 443 B |
After Width: | Height: | Size: 740 B |
After Width: | Height: | Size: 872 B |
After Width: | Height: | Size: 443 B |
After Width: | Height: | Size: 580 B |
After Width: | Height: | Size: 872 B |
After Width: | Height: | Size: 1.2 KiB |
10
app/src/main/res/drawable/baseline_swap_calls_24.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M18,4l-4,4h3v7c0,1.1 -0.9,2 -2,2s-2,-0.9 -2,-2V8c0,-2.21 -1.79,-4 -4,-4S5,5.79 5,8v7H2l4,4 4,-4H7V8c0,-1.1 0.9,-2 2,-2s2,0.9 2,2v7c0,2.21 1.79,4 4,4s4,-1.79 4,-4V8h3l-4,-4z"/>
|
||||||
|
</vector>
|
@ -18,4 +18,15 @@
|
|||||||
android:icon="@drawable/ic_delete_white_24dp"
|
android:icon="@drawable/ic_delete_white_24dp"
|
||||||
app:showAsAction="ifRoom"/>
|
app:showAsAction="ifRoom"/>
|
||||||
|
|
||||||
|
<item android:id="@+id/action_select_all"
|
||||||
|
android:title="@string/action_select_all"
|
||||||
|
android:icon="@drawable/ic_select_all_white_24dp"
|
||||||
|
app:showAsAction="ifRoom"/>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_migrate"
|
||||||
|
android:icon="@drawable/baseline_swap_calls_white_24"
|
||||||
|
android:title="@string/label_migration"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
</menu>
|
</menu>
|