mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-11-19 23:29:16 +01:00
Open from homescreen/add shortcut to launcher (#435)
* Add very basic "Add to homescreen" action in manga info fragment. * Fix open from homescreen opening current manga (if a manga is open). Code cleanup. * Improve fix for "Opening from homescreen opens currently open manga if a manga is currently open" and fix "Going back to the main app via a Manga opened through a shortcut repeats the launcher open animation". * Implement custom icons, add star icon and optimize some things. * Remove Tachiyomi and custom image icon types. * Move icon creation task into an observable. Added some extra error handling.
This commit is contained in:
parent
a81609fd2c
commit
d352405ba6
@ -153,6 +153,8 @@ dependencies {
|
|||||||
// Image library
|
// Image library
|
||||||
compile 'com.github.bumptech.glide:glide:3.7.0'
|
compile 'com.github.bumptech.glide:glide:3.7.0'
|
||||||
compile 'com.github.bumptech.glide:okhttp3-integration:1.4.0@aar'
|
compile 'com.github.bumptech.glide:okhttp3-integration:1.4.0@aar'
|
||||||
|
// Transformations
|
||||||
|
compile 'jp.wasabeef:glide-transformations:2.0.1'
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
compile 'com.jakewharton.timber:timber:4.3.0'
|
compile 'com.jakewharton.timber:timber:4.3.0'
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||||
|
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".App"
|
android:name=".App"
|
||||||
@ -28,7 +29,8 @@
|
|||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.manga.MangaActivity"
|
android:name=".ui.manga.MangaActivity"
|
||||||
android:parentActivityName=".ui.main.MainActivity" >
|
android:parentActivityName=".ui.main.MainActivity"
|
||||||
|
android:exported="true">
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.reader.ReaderActivity"
|
android:name=".ui.reader.ReaderActivity"
|
||||||
|
@ -24,6 +24,7 @@ class MangaActivity : BaseRxActivity<MangaPresenter>() {
|
|||||||
|
|
||||||
const val FROM_CATALOGUE_EXTRA = "from_catalogue"
|
const val FROM_CATALOGUE_EXTRA = "from_catalogue"
|
||||||
const val MANGA_EXTRA = "manga"
|
const val MANGA_EXTRA = "manga"
|
||||||
|
const val FROM_LAUNCHER_EXTRA = "from_launcher"
|
||||||
const val INFO_FRAGMENT = 0
|
const val INFO_FRAGMENT = 0
|
||||||
const val CHAPTERS_FRAGMENT = 1
|
const val CHAPTERS_FRAGMENT = 1
|
||||||
const val MYANIMELIST_FRAGMENT = 2
|
const val MYANIMELIST_FRAGMENT = 2
|
||||||
@ -47,6 +48,11 @@ class MangaActivity : BaseRxActivity<MangaPresenter>() {
|
|||||||
super.onCreate(savedState)
|
super.onCreate(savedState)
|
||||||
setContentView(R.layout.activity_manga)
|
setContentView(R.layout.activity_manga)
|
||||||
|
|
||||||
|
val fromLauncher = intent.getBooleanExtra(FROM_LAUNCHER_EXTRA, false)
|
||||||
|
|
||||||
|
//Remove any current manga if we are launching from launcher
|
||||||
|
if(fromLauncher) SharedData.remove(MangaEvent::class.java)
|
||||||
|
|
||||||
presenter.setMangaEvent(SharedData.getOrPut(MangaEvent::class.java) {
|
presenter.setMangaEvent(SharedData.getOrPut(MangaEvent::class.java) {
|
||||||
val id = intent.getLongExtra(MANGA_EXTRA, 0)
|
val id = intent.getLongExtra(MANGA_EXTRA, 0)
|
||||||
MangaEvent(presenter.db.getManga(id).executeAsBlocking()!!)
|
MangaEvent(presenter.db.getManga(id).executeAsBlocking()!!)
|
||||||
|
@ -1,21 +1,44 @@
|
|||||||
package eu.kanade.tachiyomi.ui.manga.info
|
package eu.kanade.tachiyomi.ui.manga.info
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.graphics.drawable.BitmapDrawable
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.customtabs.CustomTabsIntent
|
import android.support.customtabs.CustomTabsIntent
|
||||||
|
import android.support.design.widget.Snackbar
|
||||||
|
import android.util.SparseArray
|
||||||
import android.view.*
|
import android.view.*
|
||||||
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
|
import com.bumptech.glide.BitmapRequestBuilder
|
||||||
|
import com.bumptech.glide.BitmapTypeRequest
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||||
|
import com.bumptech.glide.load.resource.bitmap.CenterCrop
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
import eu.kanade.tachiyomi.data.source.Source
|
import eu.kanade.tachiyomi.data.source.Source
|
||||||
import eu.kanade.tachiyomi.data.source.online.OnlineSource
|
import eu.kanade.tachiyomi.data.source.online.OnlineSource
|
||||||
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment
|
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment
|
||||||
|
import eu.kanade.tachiyomi.ui.library.LibraryFragment
|
||||||
|
import eu.kanade.tachiyomi.ui.manga.MangaActivity
|
||||||
import eu.kanade.tachiyomi.util.getResourceColor
|
import eu.kanade.tachiyomi.util.getResourceColor
|
||||||
import eu.kanade.tachiyomi.util.toast
|
import eu.kanade.tachiyomi.util.toast
|
||||||
|
import jp.wasabeef.glide.transformations.CropCircleTransformation
|
||||||
|
import jp.wasabeef.glide.transformations.CropSquareTransformation
|
||||||
|
import jp.wasabeef.glide.transformations.MaskTransformation
|
||||||
|
import jp.wasabeef.glide.transformations.RoundedCornersTransformation
|
||||||
import kotlinx.android.synthetic.main.fragment_manga_info.*
|
import kotlinx.android.synthetic.main.fragment_manga_info.*
|
||||||
|
import kotlinx.android.synthetic.main.item_download.*
|
||||||
import nucleus.factory.RequiresPresenter
|
import nucleus.factory.RequiresPresenter
|
||||||
|
import rx.Observable
|
||||||
|
import rx.android.schedulers.AndroidSchedulers
|
||||||
|
import rx.schedulers.Schedulers
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.io.IOException
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fragment that shows manga information.
|
* Fragment that shows manga information.
|
||||||
@ -34,6 +57,7 @@ class MangaInfoFragment : BaseRxFragment<MangaInfoPresenter>() {
|
|||||||
fun newInstance(): MangaInfoFragment {
|
fun newInstance(): MangaInfoFragment {
|
||||||
return MangaInfoFragment()
|
return MangaInfoFragment()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedState: Bundle?) {
|
override fun onCreate(savedState: Bundle?) {
|
||||||
@ -61,6 +85,7 @@ class MangaInfoFragment : BaseRxFragment<MangaInfoPresenter>() {
|
|||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.action_open_in_browser -> openInBrowser()
|
R.id.action_open_in_browser -> openInBrowser()
|
||||||
R.id.action_share -> shareManga()
|
R.id.action_share -> shareManga()
|
||||||
|
R.id.action_add_to_home_screen -> addToHomeScreen()
|
||||||
else -> return super.onOptionsItemSelected(item)
|
else -> return super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@ -178,6 +203,80 @@ class MangaInfoFragment : BaseRxFragment<MangaInfoPresenter>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the manga to the home screen
|
||||||
|
*/
|
||||||
|
fun addToHomeScreen() {
|
||||||
|
val shortcutIntent = activity.intent
|
||||||
|
shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||||
|
.putExtra(MangaActivity.FROM_LAUNCHER_EXTRA, true)
|
||||||
|
|
||||||
|
val addIntent = Intent()
|
||||||
|
addIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent)
|
||||||
|
.action = "com.android.launcher.action.INSTALL_SHORTCUT"
|
||||||
|
|
||||||
|
//Set shortcut title
|
||||||
|
MaterialDialog.Builder(activity)
|
||||||
|
.title(R.string.shortcut_title)
|
||||||
|
.input("", presenter.manga.title, { md, text ->
|
||||||
|
//Set shortcut title
|
||||||
|
addIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, text.toString())
|
||||||
|
|
||||||
|
reshapeIconBitmap(addIntent,
|
||||||
|
Glide.with(context).load(presenter.manga).asBitmap())
|
||||||
|
})
|
||||||
|
.negativeText(android.R.string.cancel)
|
||||||
|
.onNegative { materialDialog, dialogAction -> materialDialog.cancel() }
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun reshapeIconBitmap(addIntent: Intent, request: BitmapTypeRequest<out Any>) {
|
||||||
|
val modes = intArrayOf(R.string.circular_icon,
|
||||||
|
R.string.rounded_icon,
|
||||||
|
R.string.square_icon,
|
||||||
|
R.string.star_icon)
|
||||||
|
|
||||||
|
fun BitmapRequestBuilder<out Any, Bitmap>.toIcon(): Bitmap {
|
||||||
|
return this.into(96, 96).get()
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialDialog.Builder(activity)
|
||||||
|
.title(R.string.icon_shape)
|
||||||
|
.negativeText(android.R.string.cancel)
|
||||||
|
.items(modes.map { getString(it) })
|
||||||
|
.itemsCallback { dialog, view, i, charSequence ->
|
||||||
|
Observable.fromCallable {
|
||||||
|
// i = 0: Circular icon
|
||||||
|
// i = 1: Rounded icon
|
||||||
|
// i = 2: Square icon
|
||||||
|
// i = 3: Star icon (because boredom)
|
||||||
|
when (i) {
|
||||||
|
0 -> request.transform(CropCircleTransformation(context)).toIcon()
|
||||||
|
1 -> request.transform(RoundedCornersTransformation(context, 5, 0)).toIcon()
|
||||||
|
2 -> request.transform(CropSquareTransformation(context)).toIcon()
|
||||||
|
3 -> request.transform(CenterCrop(context), MaskTransformation(context, R.drawable.mask_star)).toIcon()
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe({ if (it != null) createShortcut(addIntent, it) },
|
||||||
|
{ context.toast(R.string.icon_creation_fail) })
|
||||||
|
}.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createShortcut(addIntent: Intent, icon: Bitmap) {
|
||||||
|
//Send shortcut intent
|
||||||
|
addIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, icon)
|
||||||
|
context.sendBroadcast(addIntent)
|
||||||
|
//Go to launcher to show this shiny new shortcut!
|
||||||
|
val startMain = Intent(Intent.ACTION_MAIN)
|
||||||
|
startMain.addCategory(Intent.CATEGORY_HOME)
|
||||||
|
.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
activity.runOnUiThread {
|
||||||
|
startActivity(startMain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update FAB with correct drawable.
|
* Update FAB with correct drawable.
|
||||||
*
|
*
|
||||||
|
9
app/src/main/res/drawable/ic_home_white_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_home_white_24dp.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"/>
|
||||||
|
</vector>
|
BIN
app/src/main/res/drawable/mask_star.png
Normal file
BIN
app/src/main/res/drawable/mask_star.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 585 B |
@ -12,4 +12,8 @@
|
|||||||
android:title="@string/action_open_in_browser"
|
android:title="@string/action_open_in_browser"
|
||||||
app:showAsAction="never"/>
|
app:showAsAction="never"/>
|
||||||
|
|
||||||
|
<item android:id="@+id/action_add_to_home_screen"
|
||||||
|
android:title="@string/action_add_to_home_screen"
|
||||||
|
app:showAsAction="never"/>
|
||||||
|
|
||||||
</menu>
|
</menu>
|
@ -51,6 +51,7 @@
|
|||||||
<string name="action_remove">Remove</string>
|
<string name="action_remove">Remove</string>
|
||||||
<string name="action_resume">Resume</string>
|
<string name="action_resume">Resume</string>
|
||||||
<string name="action_open_in_browser">Open in browser</string>
|
<string name="action_open_in_browser">Open in browser</string>
|
||||||
|
<string name="action_add_to_home_screen">Add to home screen</string>
|
||||||
<string name="action_display_mode">Change display mode</string>
|
<string name="action_display_mode">Change display mode</string>
|
||||||
<string name="action_set_filter">Set filter</string>
|
<string name="action_set_filter">Set filter</string>
|
||||||
<string name="action_cancel">Cancel</string>
|
<string name="action_cancel">Cancel</string>
|
||||||
@ -225,6 +226,16 @@
|
|||||||
<string name="manga_info_genres_label">Genres</string>
|
<string name="manga_info_genres_label">Genres</string>
|
||||||
<string name="share_subject">Share…</string>
|
<string name="share_subject">Share…</string>
|
||||||
<string name="share_text">Check out %1$s! at %2$s</string>
|
<string name="share_text">Check out %1$s! at %2$s</string>
|
||||||
|
<string name="added_to_home_screen">Manga added to home screen</string>
|
||||||
|
<string name="icon_type">Icon type</string>
|
||||||
|
<string name="tachiyomi_icon">Tachiyomi icon</string>
|
||||||
|
<string name="circular_icon">Circular icon</string>
|
||||||
|
<string name="rounded_icon">Rounded icon</string>
|
||||||
|
<string name="square_icon">Square icon</string>
|
||||||
|
<string name="star_icon">Star icon</string>
|
||||||
|
<string name="shortcut_title">Shortcut title</string>
|
||||||
|
<string name="icon_shape">Icon shape</string>
|
||||||
|
<string name="icon_creation_fail">Failed to create shortcut!</string>
|
||||||
|
|
||||||
<!-- Manga chapters fragment -->
|
<!-- Manga chapters fragment -->
|
||||||
<string name="manga_chapters_tab">Chapters</string>
|
<string name="manga_chapters_tab">Chapters</string>
|
||||||
@ -305,6 +316,7 @@
|
|||||||
<!-- File Picker Titles -->
|
<!-- File Picker Titles -->
|
||||||
<string name="file_select_cover">Select cover image</string>
|
<string name="file_select_cover">Select cover image</string>
|
||||||
<string name="file_select_backup">Select backup file</string>
|
<string name="file_select_backup">Select backup file</string>
|
||||||
|
<string name="file_select_icon">Select shortcut icon</string>
|
||||||
|
|
||||||
<!--UpdateCheck-->
|
<!--UpdateCheck-->
|
||||||
<string name="update_check_title">New update available!</string>
|
<string name="update_check_title">New update available!</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user